@moamc/rn-cli 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 MOAMC Engineering Team
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 ADDED
@@ -0,0 +1,154 @@
1
+ # @moamc/rn-cli
2
+
3
+ Enterprise-grade Code Generation CLI for React Native Applications
4
+
5
+ ## šŸš€ Features
6
+
7
+ - āœ… Generate complete feature modules with screens, hooks, and API integration
8
+ - āœ… Add screens to existing features with form support
9
+ - āœ… Create API services with GET/POST methods
10
+ - āœ… Generate React Query hooks with validation
11
+ - āœ… Support for multiple field types (text, number, email, date, dropdown, etc.)
12
+ - āœ… Automatic validation generation
13
+ - āœ… TypeScript-ready templates
14
+
15
+ ## šŸ“¦ Installation
16
+
17
+ ### Global Installation (Recommended)
18
+
19
+ ```bash
20
+ npm install -g @moamc/rn-cli
21
+ ```
22
+
23
+ ### Project-specific Installation
24
+
25
+ ```bash
26
+ npm install --save-dev @moamc/rn-cli
27
+ ```
28
+
29
+ ## šŸŽÆ Usage
30
+
31
+ After installation, run the CLI from your React Native project root:
32
+
33
+ ```bash
34
+ rn-cli
35
+ ```
36
+
37
+ ### Available Commands
38
+
39
+ #### Generate Feature
40
+ ```bash
41
+ rn-cli generate:feature
42
+ # or
43
+ rn-cli g:f
44
+ ```
45
+ Creates a complete feature module with screen, API service, and hooks.
46
+
47
+ #### Generate Screen
48
+ ```bash
49
+ rn-cli generate:screen
50
+ # or
51
+ rn-cli g:s
52
+ ```
53
+ Adds a new screen to an existing feature with optional form support.
54
+
55
+ #### Generate API
56
+ ```bash
57
+ rn-cli generate:api
58
+ # or
59
+ rn-cli g:a
60
+ ```
61
+ Creates API service methods with React Query integration.
62
+
63
+ #### Generate Hook
64
+ ```bash
65
+ rn-cli generate:hook
66
+ # or
67
+ rn-cli g:h
68
+ ```
69
+ Generates data-fetching hooks with validation.
70
+
71
+ ## šŸ“ Project Structure Requirements
72
+
73
+ Your React Native project should follow this structure:
74
+
75
+ ```
76
+ project-root/
77
+ ā”œā”€ā”€ screens/
78
+ │ └── FeatureName/
79
+ │ └── ScreenName/
80
+ │ ā”œā”€ā”€ ScreenName.js
81
+ │ ā”œā”€ā”€ hooks/
82
+ │ ā”œā”€ā”€ components/
83
+ │ ā”œā”€ā”€ validation.js
84
+ │ ā”œā”€ā”€ constants.js
85
+ │ └── helpers.js
86
+ ā”œā”€ā”€ services/
87
+ ā”œā”€ā”€ queries/
88
+ └── navigations/
89
+ ```
90
+
91
+ ## šŸŽØ Form Field Types
92
+
93
+ - **Text** - Standard text input
94
+ - **Number** - Numeric input
95
+ - **Email** - Email address with validation
96
+ - **Date** - Date picker
97
+ - **Dropdown** - Select from options
98
+ - **Radio** - Single choice
99
+ - **Checkbox** - Multiple choices
100
+ - **File** - File upload
101
+
102
+ ## šŸ”Œ API Integration
103
+
104
+ The CLI supports both GET and POST APIs:
105
+
106
+ 1. **GET API** - Fetch data with React Query
107
+ 2. **POST API** - Submit forms with useMutation
108
+ 3. **Automatic cache invalidation** after successful POST
109
+
110
+ ## ✨ Features
111
+
112
+ ### Automatic Validation
113
+ - Real-time field validation
114
+ - Submit-time validation
115
+ - Custom validation rules per field type
116
+
117
+ ### React Query Integration
118
+ - Automatic query key generation
119
+ - Cache management
120
+ - Error handling
121
+ - Loading states
122
+
123
+ ### Form Support
124
+ - Multiple field types
125
+ - Field width control (full/half)
126
+ - Required field validation
127
+ - Dropdown options
128
+
129
+ ## šŸ› ļø Development
130
+
131
+ To use in development mode:
132
+
133
+ ```bash
134
+ cd cli-tool
135
+ npm link
136
+ ```
137
+
138
+ Then in your project:
139
+
140
+ ```bash
141
+ rn-cli
142
+ ```
143
+
144
+ ## šŸ“ License
145
+
146
+ MIT
147
+
148
+ ## šŸ‘„ Author
149
+
150
+ MOAMC Engineering Team
151
+
152
+ ## šŸ¤ Contributing
153
+
154
+ Contributions are welcome! Please feel free to submit a Pull Request.
@@ -0,0 +1,155 @@
1
+ const inquirer = require('inquirer');
2
+ const chalk = require('chalk');
3
+ const path = require('path');
4
+ const fs = require('fs-extra');
5
+ const {
6
+ getProjectPath,
7
+ readFile,
8
+ writeFile,
9
+ logSuccess,
10
+ logInfo,
11
+ logWarning,
12
+ toCamelCase,
13
+ } = require('../utils/fileUtils');
14
+
15
+ const generateAPI = async () => {
16
+ console.log(chalk.cyan.bold('\n╭────────────────────────────────────────────────────────────╮'));
17
+ console.log(chalk.cyan.bold('│') + chalk.white.bold(' šŸ”Œ API METHOD GENERATOR ') + chalk.cyan.bold('│'));
18
+ console.log(chalk.cyan.bold('╰────────────────────────────────────────────────────────────╯'));
19
+ console.log(chalk.gray(' Create a new API service method\n'));
20
+
21
+ const servicesPath = getProjectPath('services');
22
+ const services = fs.readdirSync(servicesPath).filter(file => {
23
+ const fullPath = path.join(servicesPath, file);
24
+ return fs.statSync(fullPath).isDirectory() && !['common'].includes(file);
25
+ });
26
+
27
+ const answers = await inquirer.prompt([
28
+ {
29
+ type: 'list',
30
+ name: 'serviceName',
31
+ message: chalk.yellow('ā“ Select the target service:'),
32
+ choices: [...services, chalk.green('+ Create new service')],
33
+ prefix: chalk.green('āœ“'),
34
+ },
35
+ {
36
+ type: 'input',
37
+ name: 'newServiceName',
38
+ message: chalk.yellow('ā“ Enter new service name:'),
39
+ when: (answers) => answers.serviceName.includes('Create new service'),
40
+ validate: (input) => input.trim() !== '' || chalk.red('āš ļø Service name is required'),
41
+ prefix: chalk.green('āœ“'),
42
+ },
43
+ {
44
+ type: 'input',
45
+ name: 'methodName',
46
+ message: chalk.yellow('ā“ Enter method name') + chalk.gray(' (e.g., getUserProfile, saveBankDetails):'),
47
+ validate: (input) => input.trim() !== '' || chalk.red('āš ļø Method name is required'),
48
+ prefix: chalk.green('āœ“'),
49
+ },
50
+ {
51
+ type: 'input',
52
+ name: 'endpoint',
53
+ message: chalk.yellow('ā“ Enter API endpoint') + chalk.gray(' (e.g., /api/user/profile):'),
54
+ validate: (input) => input.trim() !== '' || chalk.red('āš ļø Endpoint is required'),
55
+ prefix: chalk.green('āœ“'),
56
+ },
57
+ {
58
+ type: 'list',
59
+ name: 'methodType',
60
+ message: chalk.yellow('ā“ Select HTTP method:'),
61
+ choices: [
62
+ { name: chalk.green('POST') + chalk.gray(' - Create or update data'), value: 'POST' },
63
+ { name: chalk.blue('GET') + chalk.gray(' - Retrieve data'), value: 'GET' },
64
+ { name: chalk.yellow('PUT') + chalk.gray(' - Update existing data'), value: 'PUT' },
65
+ { name: chalk.red('DELETE') + chalk.gray(' - Remove data'), value: 'DELETE' },
66
+ ],
67
+ default: 'POST',
68
+ prefix: chalk.green('āœ“'),
69
+ },
70
+ {
71
+ type: 'confirm',
72
+ name: 'requiresAuth',
73
+ message: chalk.yellow('ā“ Requires authentication?'),
74
+ default: true,
75
+ prefix: chalk.green('āœ“'),
76
+ },
77
+ ]);
78
+
79
+ const serviceName = answers.serviceName.includes('Create new service')
80
+ ? answers.newServiceName.toLowerCase()
81
+ : answers.serviceName;
82
+
83
+ const serviceFile = getProjectPath('services', serviceName, `${serviceName}.js`);
84
+ const methodName = toCamelCase(answers.methodName);
85
+
86
+ let newMethod = '';
87
+ if (answers.methodType === 'GET') {
88
+ newMethod = `
89
+ function ${methodName}(data) {
90
+ \tconst response = coreAPI.makeAuthenticatedGetCall(
91
+ \t\t'${answers.endpoint}',
92
+ \t\t${answers.requiresAuth}
93
+ \t);
94
+ \treturn handleResponse(response);
95
+ }`;
96
+ } else {
97
+ newMethod = `
98
+ function ${methodName}(data) {
99
+ \tconst response = coreAPI.makeAuthenticatedPostCall(
100
+ \t\t'${answers.endpoint}',
101
+ \t\tdata,
102
+ \t\t${answers.requiresAuth}
103
+ \t);
104
+ \treturn handleResponse(response);
105
+ }`;
106
+ }
107
+
108
+ if (fs.existsSync(serviceFile)) {
109
+ let content = readFile(serviceFile);
110
+
111
+ // Find the export statement
112
+ const exportMatch = content.match(/export const \w+Service = \{([^}]*)\};/s);
113
+
114
+ if (exportMatch) {
115
+ const exportContent = exportMatch[1].trim();
116
+ const methods = exportContent.split(',').map(m => m.trim()).filter(m => m);
117
+
118
+ // Add new method before the export
119
+ const insertPosition = content.lastIndexOf('export const');
120
+ content = content.slice(0, insertPosition) + newMethod + '\n\n' + content.slice(insertPosition);
121
+
122
+ // Update export
123
+ methods.push(methodName);
124
+ const newExport = `export const ${serviceName}Service = {\n\t${methods.join(',\n\t')}\n};`;
125
+ content = content.replace(/export const \w+Service = \{[^}]*\};/s, newExport);
126
+
127
+ writeFile(serviceFile, content);
128
+ logSuccess(`Added method "${methodName}" to ${serviceName} service`);
129
+ } else {
130
+ logWarning('Could not find export statement. Please add the method manually.');
131
+ }
132
+ } else {
133
+ // Create new service file
134
+ const content = `import { coreAPI } from '../common/baseConfig';
135
+ import handleResponse from '../handler';
136
+ ${newMethod}
137
+
138
+ export const ${serviceName}Service = {
139
+ \t${methodName}
140
+ };
141
+ `;
142
+ writeFile(serviceFile, content);
143
+ logSuccess(`Created new service: ${serviceName}`);
144
+ }
145
+
146
+ logSuccess(`\n✨ API method "${methodName}" added successfully!`);
147
+ console.log(chalk.cyan('\n╭────────────────────────────────────────────────────────────╮'));
148
+ console.log(chalk.cyan('│') + chalk.white.bold(' šŸ“ NEXT STEPS ') + chalk.cyan('│'));
149
+ console.log(chalk.cyan('╰────────────────────────────────────────────────────────────╯'));
150
+ console.log(chalk.white(' 1. ') + chalk.green('āœ“') + chalk.gray(' Create query options in /queries if using React Query'));
151
+ console.log(chalk.white(' 2. ') + chalk.green('āœ“') + chalk.gray(' Use the method in your custom hook'));
152
+ console.log(chalk.cyan('\n' + '─'.repeat(65) + '\n'));
153
+ };
154
+
155
+ module.exports = { generateAPI };
@@ -0,0 +1,155 @@
1
+ const inquirer = require('inquirer');
2
+ const chalk = require('chalk');
3
+ const path = require('path');
4
+ const {
5
+ getProjectPath,
6
+ writeFile,
7
+ logSuccess,
8
+ logInfo,
9
+ toPascalCase,
10
+ confirmOverwrite,
11
+ fileExists,
12
+ } = require('../utils/fileUtils');
13
+ const { generateScreenTemplate } = require('../templates/screenTemplate');
14
+ const { generateHookTemplate } = require('../templates/hookTemplate');
15
+ const { generateValidationTemplate } = require('../templates/validationTemplate');
16
+ const { generateStylesTemplate } = require('../templates/stylesTemplate');
17
+ const { generateConstantsTemplate } = require('../templates/constantsTemplate');
18
+ const { generateHelpersTemplate } = require('../templates/helpersTemplate');
19
+ const { generateAPIServiceTemplate } = require('../templates/apiTemplate');
20
+
21
+ const generateFeature = async () => {
22
+ console.log(chalk.cyan.bold('\n╭────────────────────────────────────────────────────────────╮'));
23
+ console.log(chalk.cyan.bold('│') + chalk.white.bold(' šŸŽÆ FEATURE CREATION WIZARD ') + chalk.cyan.bold('│'));
24
+ console.log(chalk.cyan.bold('╰────────────────────────────────────────────────────────────╯'));
25
+ console.log(chalk.gray(' Let\'s build a new feature module for your application\n'));
26
+
27
+ const answers = await inquirer.prompt([
28
+ {
29
+ type: 'input',
30
+ name: 'featureName',
31
+ message: chalk.yellow('ā“ Enter feature name') + chalk.gray(' (e.g., UserProfile, BankManagement):'),
32
+ validate: (input) => input.trim() !== '' || chalk.red('āš ļø Feature name is required'),
33
+ prefix: chalk.green('āœ“'),
34
+ },
35
+ {
36
+ type: 'input',
37
+ name: 'screenName',
38
+ message: chalk.yellow('ā“ Enter main screen name') + chalk.gray(' (e.g., ProfileDetails, AddBank):'),
39
+ validate: (input) => input.trim() !== '' || chalk.red('āš ļø Screen name is required'),
40
+ prefix: chalk.green('āœ“'),
41
+ },
42
+ {
43
+ type: 'confirm',
44
+ name: 'hasForm',
45
+ message: chalk.yellow('ā“ Does this screen include a form?'),
46
+ default: false,
47
+ prefix: chalk.green('āœ“'),
48
+ },
49
+ {
50
+ type: 'confirm',
51
+ name: 'createAPI',
52
+ message: chalk.yellow('ā“ Would you like to create an API service?'),
53
+ default: true,
54
+ prefix: chalk.green('āœ“'),
55
+ },
56
+ {
57
+ type: 'input',
58
+ name: 'apiMethodName',
59
+ message: chalk.yellow('ā“ Enter API method name') + chalk.gray(' (e.g., getUserProfile, saveBankDetails):'),
60
+ when: (answers) => answers.createAPI,
61
+ validate: (input) => input.trim() !== '' || chalk.red('āš ļø API method name is required'),
62
+ prefix: chalk.green('āœ“'),
63
+ },
64
+ {
65
+ type: 'input',
66
+ name: 'apiEndpoint',
67
+ message: chalk.yellow('ā“ Enter API endpoint') + chalk.gray(' (e.g., /api/user/profile):'),
68
+ when: (answers) => answers.createAPI,
69
+ validate: (input) => input.trim() !== '' || chalk.red('āš ļø API endpoint is required'),
70
+ prefix: chalk.green('āœ“'),
71
+ },
72
+ {
73
+ type: 'list',
74
+ name: 'apiType',
75
+ message: chalk.yellow('ā“ Select API method type:'),
76
+ choices: [
77
+ { name: chalk.green('POST') + chalk.gray(' - Create or update data'), value: 'POST' },
78
+ { name: chalk.blue('GET') + chalk.gray(' - Retrieve data'), value: 'GET' },
79
+ ],
80
+ when: (answers) => answers.createAPI,
81
+ default: 'POST',
82
+ prefix: chalk.green('āœ“'),
83
+ },
84
+ ]);
85
+
86
+ const featureName = toPascalCase(answers.featureName);
87
+ const screenName = toPascalCase(answers.screenName);
88
+ const screenPath = getProjectPath('screens', featureName, screenName);
89
+
90
+ // Create screen files
91
+ const screenFile = path.join(screenPath, `${screenName}.js`);
92
+ const hookFile = path.join(screenPath, `use${screenName}.js`);
93
+ const stylesFile = path.join(screenPath, 'styles.js');
94
+ const constantsFile = path.join(screenPath, 'constants.js');
95
+ const helpersFile = path.join(screenPath, 'helpers.js');
96
+
97
+ const files = [
98
+ { path: screenFile, content: generateScreenTemplate(screenName, featureName, answers.hasForm, formFields) },
99
+ { path: hookFile, content: generateHookTemplate(screenName, answers.hasForm, formFields) },
100
+ { path: stylesFile, content: generateStylesTemplate() },
101
+ { path: constantsFile, content: generateConstantsTemplate(formFields) },
102
+ { path: helpersFile, content: generateHelpersTemplate() },
103
+ ];
104
+
105
+ if (answers.hasForm) {
106
+ const validationFile = path.join(screenPath, 'validation.js');
107
+ files.push({ path: validationFile, content: generateValidationTemplate(formFields) });
108
+ }
109
+
110
+ for (const file of files) {
111
+ if (fileExists(file.path)) {
112
+ const shouldOverwrite = await confirmOverwrite(inquirer, file.path);
113
+ if (!shouldOverwrite) {
114
+ logInfo(`Skipped: ${path.basename(file.path)}`);
115
+ continue;
116
+ }
117
+ }
118
+ writeFile(file.path, file.content);
119
+ logSuccess(`Created: ${path.relative(getProjectPath(), file.path)}`);
120
+ }
121
+
122
+ // Create API service if requested
123
+ if (answers.createAPI) {
124
+ const serviceName = featureName.toLowerCase();
125
+ const servicePath = getProjectPath('services', serviceName);
126
+ const serviceFile = path.join(servicePath, `${serviceName}.js`);
127
+
128
+ const methods = [{
129
+ name: answers.apiMethodName,
130
+ endpoint: answers.apiEndpoint,
131
+ type: answers.apiType,
132
+ }];
133
+
134
+ const serviceContent = generateAPIServiceTemplate(featureName, methods);
135
+
136
+ if (fileExists(serviceFile)) {
137
+ logInfo(`Service file already exists: ${serviceFile}`);
138
+ logInfo('Please manually add the new method to the existing service file.');
139
+ } else {
140
+ writeFile(serviceFile, serviceContent);
141
+ logSuccess(`Created: ${path.relative(getProjectPath(), serviceFile)}`);
142
+ }
143
+ }
144
+
145
+ logSuccess(`\n✨ Feature "${featureName}" created successfully!`);
146
+ console.log(chalk.cyan('\n╭────────────────────────────────────────────────────────────╮'));
147
+ console.log(chalk.cyan('│') + chalk.white.bold(' šŸ“ NEXT STEPS ') + chalk.cyan('│'));
148
+ console.log(chalk.cyan('╰────────────────────────────────────────────────────────────╯'));
149
+ console.log(chalk.white(' 1. ') + chalk.green('āœ“') + chalk.gray(' Add the screen to Navigation.js'));
150
+ console.log(chalk.white(' 2. ') + chalk.green('āœ“') + chalk.gray(' Update the API service if needed'));
151
+ console.log(chalk.white(' 3. ') + chalk.green('āœ“') + chalk.gray(' Create query options in /queries if using React Query'));
152
+ console.log(chalk.cyan('\n' + '─'.repeat(65) + '\n'));
153
+ };
154
+
155
+ module.exports = { generateFeature };
@@ -0,0 +1,115 @@
1
+ const inquirer = require('inquirer');
2
+ const chalk = require('chalk');
3
+ const path = require('path');
4
+ const fs = require('fs-extra');
5
+ const {
6
+ getProjectPath,
7
+ writeFile,
8
+ logSuccess,
9
+ logInfo,
10
+ toPascalCase,
11
+ confirmOverwrite,
12
+ fileExists,
13
+ } = require('../utils/fileUtils');
14
+ const { generateHookTemplate } = require('../templates/hookTemplate');
15
+
16
+ const generateHook = async () => {
17
+ console.log(chalk.cyan.bold('\n╭────────────────────────────────────────────────────────────╮'));
18
+ console.log(chalk.cyan.bold('│') + chalk.white.bold(' šŸŖ CUSTOM HOOK GENERATOR ') + chalk.cyan.bold('│'));
19
+ console.log(chalk.cyan.bold('╰────────────────────────────────────────────────────────────╯'));
20
+ console.log(chalk.gray(' Generate a reusable React hook\n'));
21
+
22
+ const screensPath = getProjectPath('screens');
23
+ const features = fs.readdirSync(screensPath).filter(file => {
24
+ const fullPath = path.join(screensPath, file);
25
+ return fs.statSync(fullPath).isDirectory();
26
+ });
27
+
28
+ const answers = await inquirer.prompt([
29
+ {
30
+ type: 'list',
31
+ name: 'location',
32
+ message: chalk.yellow('ā“ Where should the hook be created?'),
33
+ choices: [
34
+ { name: chalk.blue('šŸ“± In a screen folder'), value: 'In a screen folder' },
35
+ { name: chalk.blue('šŸ“¦ In helpers folder'), value: 'In helpers folder' },
36
+ { name: chalk.blue('šŸ“ Custom path'), value: 'Custom path' },
37
+ ],
38
+ prefix: chalk.green('āœ“'),
39
+ },
40
+ {
41
+ type: 'list',
42
+ name: 'featureName',
43
+ message: chalk.yellow('ā“ Select the feature:'),
44
+ choices: features,
45
+ when: (answers) => answers.location === 'In a screen folder',
46
+ prefix: chalk.green('āœ“'),
47
+ },
48
+ {
49
+ type: 'input',
50
+ name: 'customPath',
51
+ message: chalk.yellow('ā“ Enter custom path') + chalk.gray(' (relative to project root):'),
52
+ when: (answers) => answers.location === 'Custom path',
53
+ validate: (input) => input.trim() !== '' || chalk.red('āš ļø Path is required'),
54
+ prefix: chalk.green('āœ“'),
55
+ },
56
+ {
57
+ type: 'input',
58
+ name: 'hookName',
59
+ message: chalk.yellow('ā“ Enter hook name') + chalk.gray(' (e.g., useFetchUserData, useFormSubmit):'),
60
+ validate: (input) => {
61
+ if (input.trim() === '') return chalk.red('āš ļø Hook name is required');
62
+ if (!input.startsWith('use')) return chalk.red('āš ļø Hook name should start with "use"');
63
+ return true;
64
+ },
65
+ prefix: chalk.green('āœ“'),
66
+ },
67
+ {
68
+ type: 'list',
69
+ name: 'hookType',
70
+ message: chalk.yellow('ā“ Select hook type:'),
71
+ choices: [
72
+ { name: chalk.blue('šŸ“Š Data fetching (useQuery)'), value: 'query' },
73
+ { name: chalk.blue('šŸ“‹ Form handling (Formik)'), value: 'form' },
74
+ { name: chalk.blue('āš™ļø Custom logic'), value: 'custom' },
75
+ ],
76
+ prefix: chalk.green('āœ“'),
77
+ },
78
+ ]);
79
+
80
+ let hookPath;
81
+ if (answers.location === 'In a screen folder') {
82
+ hookPath = getProjectPath('screens', answers.featureName);
83
+ } else if (answers.location === 'In helpers folder') {
84
+ hookPath = getProjectPath('helpers');
85
+ } else {
86
+ hookPath = getProjectPath(answers.customPath);
87
+ }
88
+
89
+ const hookName = toPascalCase(answers.hookName.replace(/^use/, ''));
90
+ const hookFile = path.join(hookPath, `use${hookName}.js`);
91
+
92
+ if (fileExists(hookFile)) {
93
+ const shouldOverwrite = await confirmOverwrite(inquirer, hookFile);
94
+ if (!shouldOverwrite) {
95
+ logInfo('Operation cancelled');
96
+ return;
97
+ }
98
+ }
99
+
100
+ const hasForm = answers.hookType === 'form';
101
+ const content = generateHookTemplate(hookName, hasForm);
102
+
103
+ writeFile(hookFile, content);
104
+ logSuccess(`Created: ${path.relative(getProjectPath(), hookFile)}`);
105
+
106
+ logSuccess(`\n✨ Hook "use${hookName}" created successfully!`);
107
+ console.log(chalk.cyan('\n╭────────────────────────────────────────────────────────────╮'));
108
+ console.log(chalk.cyan('│') + chalk.white.bold(' šŸ“ NEXT STEPS ') + chalk.cyan('│'));
109
+ console.log(chalk.cyan('╰────────────────────────────────────────────────────────────╯'));
110
+ console.log(chalk.white(' 1. ') + chalk.green('āœ“') + chalk.gray(' Implement the hook logic'));
111
+ console.log(chalk.white(' 2. ') + chalk.green('āœ“') + chalk.gray(' Import and use in your component'));
112
+ console.log(chalk.cyan('\n' + '─'.repeat(65) + '\n'));
113
+ };
114
+
115
+ module.exports = { generateHook };