expo-app-ui 1.0.0 → 1.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 CHANGED
@@ -1,8 +1,25 @@
1
- # Expo UI
1
+ # Expo App UI
2
2
 
3
3
  A UI component library for Expo React Native. Copy components directly into your project and customize them to your needs.
4
4
 
5
- ## Installation
5
+ ## 📚 Documentation
6
+
7
+ **👉 [View Full Documentation →](https://expo-apps-ui.vercel.app)**
8
+
9
+ For complete documentation, usage examples, API references, and detailed instructions, visit our documentation site:
10
+
11
+ **https://expo-apps-ui.vercel.app**
12
+
13
+ The documentation includes:
14
+ - 📖 Getting Started Guide
15
+ - 🎨 Component Documentation
16
+ - 🛠️ CLI Commands Reference
17
+ - 💡 Usage Examples
18
+ - 🔧 Configuration Guides
19
+
20
+ ## Quick Start
21
+
22
+ ### Installation
6
23
 
7
24
  You can use this library directly with npx:
8
25
 
@@ -26,14 +43,14 @@ expo-app-ui add <component-name>
26
43
 
27
44
  ### Adding Components
28
45
 
29
- To add a component to your project:
46
+ Add a component to your project:
30
47
 
31
48
  ```bash
32
49
  npx expo-app-ui add custom-text
33
50
  ```
34
51
 
35
52
  This will:
36
- - Copy the component template to `components/ui/CustomText.tsx` in your project
53
+ - Copy the component template to `components/ui/custom-text.tsx` in your project
37
54
  - Automatically detect and add required dependencies (helpers, constants)
38
55
  - Preserve all imports and dependencies
39
56
  - Allow you to customize the component as needed
@@ -44,44 +61,33 @@ npx expo-app-ui add button
44
61
  # Automatically adds normalizeSize helper and theme constants if needed
45
62
  ```
46
63
 
47
- ### Adding Helpers
48
-
49
- To add a helper utility:
64
+ ### Adding Helpers, Constants, and Contexts
50
65
 
51
66
  ```bash
67
+ # Add a helper
52
68
  npx expo-app-ui add normalizeSize
53
- ```
54
-
55
- This will copy the helper to `helpers/normalizeSize.ts` in your project.
56
69
 
57
- ### Adding Constants
58
-
59
- To add constants (like theme):
60
-
61
- ```bash
70
+ # Add constants
62
71
  npx expo-app-ui add theme
63
- ```
64
-
65
- This will copy the constants to `constants/theme.ts` in your project.
66
72
 
67
- **Note:** The `theme` constant requires the `normalizeSize` helper. If you add `theme` first, you'll be prompted to add `normalizeSize`.
73
+ # Add contexts (e.g., top loading bar)
74
+ npx expo-app-ui add top-loading-bar
75
+ ```
68
76
 
69
77
  ### Listing Available Items
70
78
 
71
- To see all available components, helpers, and constants:
72
-
73
79
  ```bash
74
80
  npx expo-app-ui list
75
81
  ```
76
82
 
77
83
  ### Overwriting Existing Files
78
84
 
79
- To replace an existing component, helper, or constant:
80
-
81
85
  ```bash
82
86
  npx expo-app-ui add custom-text --overwrite
83
87
  ```
84
88
 
89
+ > 📖 **For detailed usage instructions, examples, and API documentation, visit [expo-apps-ui.vercel.app](https://expo-apps-ui.vercel.app)**
90
+
85
91
  ## Project Structure
86
92
 
87
93
  After adding components, your project structure will look like:
@@ -105,9 +111,12 @@ your-project/
105
111
  The CLI automatically detects when a component requires:
106
112
  - `normalizeSize` helper (from `@/helper/normalizeSize`)
107
113
  - `theme` constants (from `@/constants/theme`)
114
+ - Related components or contexts
108
115
 
109
116
  When you add a component that uses these dependencies, they will be automatically added to your project.
110
117
 
118
+ > 📖 **Learn more about auto-dependency detection in the [documentation](https://expo-apps-ui.vercel.app/docs/cli)**
119
+
111
120
  ## Component Templates
112
121
 
113
122
  Components are copied directly into your project, so you have full control:
@@ -119,12 +128,13 @@ Components are copied directly into your project, so you have full control:
119
128
 
120
129
  ## Path Aliases
121
130
 
122
- Components use path aliases like `@/components/ui/CustomText` and `@/constants/theme`. Make sure your Expo project has these configured:
131
+ Components use path aliases like `@/components/ui/custom-text` and `@/constants/theme`. Make sure your Expo project has these configured:
123
132
 
124
133
  **tsconfig.json:**
125
134
  ```json
126
135
  {
127
136
  "compilerOptions": {
137
+ "baseUrl": ".",
128
138
  "paths": {
129
139
  "@/*": ["./*"]
130
140
  }
@@ -149,6 +159,8 @@ module.exports = {
149
159
  };
150
160
  ```
151
161
 
162
+ > 📖 **See the [Getting Started guide](https://expo-apps-ui.vercel.app/docs/getting-started) for detailed setup instructions**
163
+
152
164
  ## Available Components
153
165
 
154
166
  - `custom-text` - A customizable Text component with font, color, and spacing props
@@ -159,6 +171,11 @@ module.exports = {
159
171
  - `progress-bar` - A progress bar component with variants
160
172
  - `marquee` - A scrolling marquee component
161
173
  - `otp-input` - An OTP input component
174
+ - `loading-bar` - An animated top loading bar component
175
+
176
+ ## Available Contexts
177
+
178
+ - `top-loading-bar-context` - React Context for managing top loading bar state
162
179
 
163
180
  ## Available Helpers
164
181
 
@@ -168,15 +185,20 @@ module.exports = {
168
185
 
169
186
  - `theme` - Theme constants including colors, fonts, and sizes
170
187
 
188
+ > 📖 **View complete documentation, props, examples, and usage for all components at [expo-apps-ui.vercel.app/docs/components](https://expo-apps-ui.vercel.app/docs/components)**
189
+
171
190
  ## Contributing
172
191
 
173
- To add new components, helpers, or constants:
192
+ To add new components, helpers, constants, or contexts:
193
+
194
+ 1. **Components**: Create a new `.tsx` file in the `templates/components/ui/` directory
195
+ 2. **Contexts**: Create a new `.tsx` file in the `templates/context/` directory
196
+ 3. **Helpers**: Create a new `.ts` file in the `templates/helpers/` directory
197
+ 4. **Constants**: Create a new `.ts` file in the `templates/constants/` directory
198
+ 5. Use kebab-case for filenames (e.g., `my-component.tsx`)
199
+ 6. The item will be automatically available via the CLI
174
200
 
175
- 1. **Components**: Create a new `.tsx` file in the `templates/` directory
176
- 2. **Helpers**: Create a new `.ts` file in the `templates/helpers/` directory
177
- 3. **Constants**: Create a new `.ts` file in the `templates/constants/` directory
178
- 4. Use kebab-case for filenames (e.g., `my-component.tsx`)
179
- 5. The item will be automatically available via the CLI
201
+ > 📖 **For contribution guidelines and best practices, visit the [documentation](https://expo-apps-ui.vercel.app)**
180
202
 
181
203
  ## License
182
204
 
@@ -1,430 +1,94 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs-extra');
4
- const path = require('path');
5
- const { program } = require('commander');
6
- const chalk = require('chalk');
7
-
8
- // Get the directory where this package is installed
9
- const getPackageDir = () => {
10
- let currentDir = __dirname;
11
-
12
- while (currentDir !== path.dirname(currentDir)) {
13
- const packageJsonPath = path.join(currentDir, 'package.json');
14
- if (fs.existsSync(packageJsonPath)) {
15
- const pkg = fs.readJsonSync(packageJsonPath);
16
- if (pkg.bin && (pkg.bin['expo-app-ui'] || pkg.name === 'expo-app-ui')) {
17
- return currentDir;
18
- }
19
- }
20
- currentDir = path.dirname(currentDir);
21
- }
22
-
23
- return path.dirname(__dirname);
24
- };
25
-
26
- const packageDir = getPackageDir();
27
- const templatesDir = path.join(packageDir, 'templates');
28
-
29
- // Get the current working directory (user's project)
30
- const getProjectRoot = () => {
31
- let currentDir = process.cwd();
32
-
33
- while (currentDir !== path.dirname(currentDir)) {
34
- if (fs.existsSync(path.join(currentDir, 'package.json')) ||
35
- fs.existsSync(path.join(currentDir, 'app.json'))) {
36
- return currentDir;
37
- }
38
- currentDir = path.dirname(currentDir);
39
- }
40
-
41
- return process.cwd();
42
- };
43
-
44
- const projectRoot = getProjectRoot();
45
-
46
- // Function to convert component name to file name
47
- const toKebabCase = (str) => {
48
- return str
49
- .replace(/([a-z])([A-Z])/g, '$1-$2')
50
- .replace(/[\s_]+/g, '-')
51
- .toLowerCase();
52
- };
53
-
54
- // Function to convert kebab-case to PascalCase
55
- const toPascalCase = (str) => {
56
- return str
57
- .split('-')
58
- .map(word => word.charAt(0).toUpperCase() + word.slice(1))
59
- .join('');
60
- };
61
-
62
- // Detect dependencies in component content
63
- const detectDependencies = (content) => {
64
- const dependencies = {
65
- needsNormalizeSize: false,
66
- needsTheme: false,
67
- };
68
-
69
- if (content.includes('@/helper/normalizeSize') || content.includes('normalizeSize')) {
70
- dependencies.needsNormalizeSize = true;
71
- }
72
-
73
- if (content.includes('@/constants/theme') || content.includes('from "@/constants/theme"')) {
74
- dependencies.needsTheme = true;
75
- }
76
-
77
- return dependencies;
78
- };
79
-
80
- // Add helper file
81
- async function addHelper(helperName, options = {}) {
82
- const kebabName = toKebabCase(helperName);
83
- const templatePath = path.join(templatesDir, 'helpers', `${kebabName}.ts`);
84
- const helpersDir = path.join(projectRoot, 'helpers');
85
- const targetPath = path.join(helpersDir, `${kebabName}.ts`);
86
-
87
- try {
88
- if (!fs.existsSync(templatePath)) {
89
- console.error(chalk.red(`✖ Helper "${helperName}" not found.`));
90
-
91
- // List available helpers
92
- const helpersTemplateDir = path.join(templatesDir, 'helpers');
93
- if (fs.existsSync(helpersTemplateDir)) {
94
- const helpers = fs.readdirSync(helpersTemplateDir)
95
- .filter(file => file.endsWith('.ts') || file.endsWith('.tsx'))
96
- .map(file => path.basename(file, path.extname(file)));
97
-
98
- if (helpers.length > 0) {
99
- console.log(chalk.yellow('\nAvailable helpers:'));
100
- helpers.forEach(helper => {
101
- console.log(chalk.gray(` - ${helper}`));
102
- });
103
- }
104
- }
105
-
106
- process.exit(1);
107
- }
108
-
109
- await fs.ensureDir(helpersDir);
110
-
111
- const overwrite = process.argv.includes('--overwrite') || options.overwrite;
112
-
113
- if (fs.existsSync(targetPath) && !overwrite) {
114
- console.error(chalk.red(`✖ Helper "${helperName}" already exists at ${targetPath}`));
115
- console.log(chalk.yellow(' Use --overwrite to replace it.'));
116
- process.exit(1);
117
- }
118
-
119
- let content = await fs.readFile(templatePath, 'utf-8');
120
- await fs.writeFile(targetPath, content, 'utf-8');
121
-
122
- if (!options.silent) {
123
- console.log(chalk.green(`✓ Added ${helperName} helper to ${path.relative(projectRoot, targetPath)}`));
124
- }
125
-
126
- return true;
127
- } catch (error) {
128
- console.error(chalk.red(`✖ Error adding helper: ${error.message}`));
129
- process.exit(1);
130
- }
131
- }
132
-
133
- // Add constants file
134
- async function addConstant(constantName, options = {}) {
135
- const kebabName = toKebabCase(constantName);
136
- const templatePath = path.join(templatesDir, 'constants', `${kebabName}.ts`);
137
- const constantsDir = path.join(projectRoot, 'constants');
138
- const targetPath = path.join(constantsDir, `${kebabName}.ts`);
139
-
140
- try {
141
- if (!fs.existsSync(templatePath)) {
142
- console.error(chalk.red(`✖ Constant "${constantName}" not found.`));
143
-
144
- // List available constants
145
- const constantsTemplateDir = path.join(templatesDir, 'constants');
146
- if (fs.existsSync(constantsTemplateDir)) {
147
- const constants = fs.readdirSync(constantsTemplateDir)
148
- .filter(file => file.endsWith('.ts') || file.endsWith('.tsx'))
149
- .map(file => path.basename(file, path.extname(file)));
150
-
151
- if (constants.length > 0) {
152
- console.log(chalk.yellow('\nAvailable constants:'));
153
- constants.forEach(constant => {
154
- console.log(chalk.gray(` - ${constant}`));
155
- });
156
- }
157
- }
158
-
159
- process.exit(1);
160
- }
161
-
162
- await fs.ensureDir(constantsDir);
163
-
164
- const overwrite = process.argv.includes('--overwrite') || options.overwrite;
165
-
166
- if (fs.existsSync(targetPath) && !overwrite) {
167
- console.error(chalk.red(`✖ Constant "${constantName}" already exists at ${targetPath}`));
168
- console.log(chalk.yellow(' Use --overwrite to replace it.'));
169
- process.exit(1);
170
- }
171
-
172
- let content = await fs.readFile(templatePath, 'utf-8');
173
-
174
- // Auto-add normalizeSize dependency for theme.ts
175
- if (kebabName === 'theme') {
176
- const normalizeSizePath = path.join(projectRoot, 'helpers', 'normalizeSize.ts');
177
- if (!fs.existsSync(normalizeSizePath)) {
178
- if (!options.silent) {
179
- console.log(chalk.blue('ℹ Adding normalizeSize helper (required dependency for theme)...'));
180
- }
181
- await addHelper('normalizeSize', { silent: true, overwrite: false });
182
- }
183
-
184
- // Fix import path to use relative path
185
- const normalizeSizeExists = fs.existsSync(normalizeSizePath);
186
- if (normalizeSizeExists) {
187
- // The import in theme.ts already uses '../helpers/normalizeSize' which is correct
188
- // No need to change it
189
- }
190
- }
3
+ /**
4
+ * Expo App UI CLI - Production Ready
5
+ *
6
+ * A UI component library CLI for Expo React Native
7
+ * Copy components directly into your project and customize them
8
+ */
191
9
 
192
- await fs.writeFile(targetPath, content, 'utf-8');
193
-
194
- if (!options.silent) {
195
- console.log(chalk.green(`✓ Added ${constantName} constant to ${path.relative(projectRoot, targetPath)}`));
196
- }
197
-
198
- return true;
199
- } catch (error) {
200
- console.error(chalk.red(`✖ Error adding constant: ${error.message}`));
201
- process.exit(1);
202
- }
203
- }
204
-
205
- // Function to add a component
206
- async function addComponent(componentName) {
207
- const kebabName = toKebabCase(componentName);
208
- const templatePath = path.join(templatesDir, `${kebabName}.tsx`);
209
- const componentsDir = path.join(projectRoot, 'components', 'ui');
210
- const targetPath = path.join(componentsDir, `${toPascalCase(kebabName)}.tsx`);
211
-
212
- try {
213
- // Check if template exists
214
- if (!fs.existsSync(templatePath)) {
215
- console.error(chalk.red(`✖ Component "${componentName}" not found.`));
216
- console.log(chalk.yellow(`Available components:`));
217
-
218
- // List available components
219
- const templates = fs.readdirSync(templatesDir)
220
- .filter(file => (file.endsWith('.tsx') || file.endsWith('.ts')) &&
221
- !fs.statSync(path.join(templatesDir, file)).isDirectory())
222
- .map(file => path.basename(file, path.extname(file)));
223
-
224
- if (templates.length === 0) {
225
- console.log(chalk.gray(' No components available.'));
226
- } else {
227
- templates.forEach(template => {
228
- console.log(chalk.gray(` - ${template}`));
229
- });
230
- }
231
- process.exit(1);
232
- }
233
-
234
- // Ensure components/ui directory exists
235
- await fs.ensureDir(componentsDir);
236
-
237
- // Get overwrite option from command
238
- const overwrite = process.argv.includes('--overwrite');
239
-
240
- // Check if component already exists
241
- if (fs.existsSync(targetPath) && !overwrite) {
242
- console.error(chalk.red(`✖ Component "${componentName}" already exists at ${targetPath}`));
243
- console.log(chalk.yellow(' Use --overwrite to replace it.'));
244
- process.exit(1);
245
- }
246
-
247
- // Read template content
248
- let content = await fs.readFile(templatePath, 'utf-8');
249
-
250
- // Detect dependencies
251
- const dependencies = detectDependencies(content);
252
-
253
- // Auto-add dependencies if needed
254
- const dependenciesToAdd = [];
255
-
256
- if (dependencies.needsNormalizeSize) {
257
- const normalizeSizePath = path.join(projectRoot, 'helpers', 'normalizeSize.ts');
258
- if (!fs.existsSync(normalizeSizePath)) {
259
- dependenciesToAdd.push('normalizeSize helper');
260
- }
261
- }
262
-
263
- if (dependencies.needsTheme) {
264
- const themePath = path.join(projectRoot, 'constants', 'theme.ts');
265
- if (!fs.existsSync(themePath)) {
266
- dependenciesToAdd.push('theme constants');
267
- }
268
- }
269
-
270
- // Show summary of what will be added
271
- if (dependenciesToAdd.length > 0) {
272
- console.log(chalk.blue(`\nℹ Detected dependencies: ${dependenciesToAdd.join(', ')}`));
273
- console.log(chalk.gray(' Adding required dependencies...\n'));
274
- }
275
-
276
- // Add dependencies
277
- if (dependencies.needsNormalizeSize) {
278
- const normalizeSizePath = path.join(projectRoot, 'helpers', 'normalizeSize.ts');
279
- if (!fs.existsSync(normalizeSizePath)) {
280
- await addHelper('normalizeSize', { silent: true });
281
- }
282
- }
283
-
284
- if (dependencies.needsTheme) {
285
- const themePath = path.join(projectRoot, 'constants', 'theme.ts');
286
- if (!fs.existsSync(themePath)) {
287
- await addConstant('theme', { silent: true });
288
- }
289
- }
290
-
291
- // Fix relative imports (e.g., CustomText from "./CustomText")
292
- // This will be handled by the user's path aliases, so we keep @/ imports
293
-
294
- // Write to target location
295
- await fs.writeFile(targetPath, content, 'utf-8');
296
-
297
- console.log(chalk.green(`✓ Added ${componentName} component to ${path.relative(projectRoot, targetPath)}`));
298
-
299
- if (dependencies.needsNormalizeSize || dependencies.needsTheme) {
300
- console.log(chalk.gray('\n💡 Tip: Make sure your project has path aliases configured for @/ imports.'));
301
- }
302
-
303
- } catch (error) {
304
- console.error(chalk.red(`✖ Error adding component: ${error.message}`));
305
- process.exit(1);
306
- }
307
- }
308
-
309
- // Function to list available items
310
- function listItems() {
311
- try {
312
- const items = {
313
- components: [],
314
- helpers: [],
315
- constants: [],
316
- };
317
-
318
- // List components
319
- if (fs.existsSync(templatesDir)) {
320
- const files = fs.readdirSync(templatesDir);
321
- files.forEach(file => {
322
- const filePath = path.join(templatesDir, file);
323
- if (fs.statSync(filePath).isFile() && (file.endsWith('.tsx') || file.endsWith('.ts'))) {
324
- items.components.push(path.basename(file, path.extname(file)));
325
- }
326
- });
327
- }
328
-
329
- // List helpers
330
- const helpersDir = path.join(templatesDir, 'helpers');
331
- if (fs.existsSync(helpersDir)) {
332
- const files = fs.readdirSync(helpersDir);
333
- files.forEach(file => {
334
- if (file.endsWith('.ts') || file.endsWith('.tsx')) {
335
- items.helpers.push(path.basename(file, path.extname(file)));
336
- }
337
- });
338
- }
339
-
340
- // List constants
341
- const constantsDir = path.join(templatesDir, 'constants');
342
- if (fs.existsSync(constantsDir)) {
343
- const files = fs.readdirSync(constantsDir);
344
- files.forEach(file => {
345
- if (file.endsWith('.ts') || file.endsWith('.tsx')) {
346
- items.constants.push(path.basename(file, path.extname(file)));
347
- }
348
- });
349
- }
350
-
351
- if (items.components.length === 0 && items.helpers.length === 0 && items.constants.length === 0) {
352
- console.log(chalk.yellow('No items available.'));
353
- return;
354
- }
355
-
356
- if (items.components.length > 0) {
357
- console.log(chalk.blue('Available components:\n'));
358
- items.components.forEach(item => {
359
- console.log(chalk.gray(` - ${item}`));
360
- });
361
- console.log();
362
- }
363
-
364
- if (items.helpers.length > 0) {
365
- console.log(chalk.blue('Available helpers:\n'));
366
- items.helpers.forEach(item => {
367
- console.log(chalk.gray(` - ${item}`));
368
- });
369
- console.log();
370
- }
371
-
372
- if (items.constants.length > 0) {
373
- console.log(chalk.blue('Available constants:\n'));
374
- items.constants.forEach(item => {
375
- console.log(chalk.gray(` - ${item}`));
376
- });
377
- }
378
- } catch (error) {
379
- console.error(chalk.red(`✖ Error listing items: ${error.message}`));
380
- process.exit(1);
381
- }
382
- }
10
+ const { program } = require('commander');
11
+ const packageJson = require('../package.json');
12
+ const { handleAdd } = require('../src/commands/add');
13
+ const { handleList } = require('../src/commands/list');
14
+ const { CLIError, TemplateNotFoundError, FileExistsError, InvalidInputError } = require('../src/utils/errors');
15
+ const Logger = require('../src/utils/logger');
16
+
17
+ // Initialize logger
18
+ const logger = new Logger({
19
+ verbose: process.argv.includes('--verbose') || process.argv.includes('-v'),
20
+ silent: process.argv.includes('--silent') || process.argv.includes('-s'),
21
+ });
383
22
 
384
23
  // CLI setup
385
24
  program
386
- .name('expo-ui')
25
+ .name('expo-app-ui')
387
26
  .description('A UI component library for Expo React Native')
388
- .version('1.0.0');
27
+ .version(packageJson.version)
28
+ .option('-v, --verbose', 'verbose output')
29
+ .option('-s, --silent', 'silent mode');
389
30
 
31
+ // Add command
390
32
  program
391
33
  .option('--overwrite', 'Overwrite existing files')
392
34
  .command('add <name>')
393
35
  .description('Add a component, helper, or constant to your project')
394
36
  .action(async (name) => {
395
- // Determine type by checking templates
396
- const kebabName = toKebabCase(name);
397
-
398
- // Check if it's a component
399
- const componentPath = path.join(templatesDir, `${kebabName}.tsx`);
400
- if (fs.existsSync(componentPath)) {
401
- await addComponent(name);
402
- return;
403
- }
404
-
405
- // Check if it's a helper
406
- const helperPath = path.join(templatesDir, 'helpers', `${kebabName}.ts`);
407
- if (fs.existsSync(helperPath)) {
408
- await addHelper(name);
409
- return;
410
- }
411
-
412
- // Check if it's a constant
413
- const constantPath = path.join(templatesDir, 'constants', `${kebabName}.ts`);
414
- if (fs.existsSync(constantPath)) {
415
- await addConstant(name);
416
- return;
37
+ try {
38
+ await handleAdd(name, {
39
+ overwrite: program.opts().overwrite || false,
40
+ verbose: logger.verbose,
41
+ silent: logger.silent,
42
+ });
43
+ } catch (error) {
44
+ if (error instanceof TemplateNotFoundError) {
45
+ logger.error(`"${name}" not found.`);
46
+ logger.info('Run "npx expo-app-ui list" to see available items.');
47
+ } else if (error instanceof FileExistsError) {
48
+ // User was prompted and chose not to overwrite, or non-interactive mode
49
+ logger.warning(`File already exists: ${error.filePath}`);
50
+ logger.info('Use --overwrite to replace it, or run the command again and choose "y" when prompted.');
51
+ } else if (error instanceof InvalidInputError) {
52
+ logger.error(error.message);
53
+ } else if (error instanceof CLIError) {
54
+ logger.error(error.message);
55
+ } else {
56
+ logger.error(`Unexpected error: ${error.message}`);
57
+ if (logger.verbose) {
58
+ console.error(error.stack);
59
+ }
60
+ }
61
+ process.exit(1);
417
62
  }
418
-
419
- // Not found
420
- console.error(chalk.red(`✖ "${name}" not found.`));
421
- console.log(chalk.yellow('Run "npx expo-app-ui list" to see available items.'));
422
- process.exit(1);
423
63
  });
424
64
 
65
+ // List command
425
66
  program
426
67
  .command('list')
427
68
  .description('List all available components, helpers, and constants')
428
- .action(listItems);
69
+ .action(() => {
70
+ try {
71
+ handleList({
72
+ verbose: logger.verbose,
73
+ silent: logger.silent,
74
+ });
75
+ } catch (error) {
76
+ logger.error(`Error: ${error.message}`);
77
+ if (logger.verbose) {
78
+ console.error(error);
79
+ }
80
+ process.exit(1);
81
+ }
82
+ });
83
+
84
+ // Global error handler
85
+ process.on('unhandledRejection', (error) => {
86
+ logger.error(`Unhandled error: ${error.message}`);
87
+ if (logger.verbose) {
88
+ console.error(error);
89
+ }
90
+ process.exit(1);
91
+ });
429
92
 
93
+ // Parse arguments
430
94
  program.parse();