@igniter-js/cli 0.1.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.
Files changed (35) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +358 -0
  3. package/dist/templates/components.json.hbs +21 -0
  4. package/dist/templates/copilot.instructions.hbs +117 -0
  5. package/dist/templates/docker-compose.hbs +15 -0
  6. package/dist/templates/env.hbs +33 -0
  7. package/dist/templates/eslintrc.hbs +3 -0
  8. package/dist/templates/feature.controller.hbs +83 -0
  9. package/dist/templates/feature.index.hbs +5 -0
  10. package/dist/templates/feature.interface.hbs +71 -0
  11. package/dist/templates/feature.procedure.hbs +76 -0
  12. package/dist/templates/globals.hbs +198 -0
  13. package/dist/templates/igniter.client.hbs +21 -0
  14. package/dist/templates/igniter.context.hbs +23 -0
  15. package/dist/templates/igniter.hbs +8 -0
  16. package/dist/templates/igniter.router.hbs +29 -0
  17. package/dist/templates/layout.hbs +54 -0
  18. package/dist/templates/page.hbs +313 -0
  19. package/dist/templates/prisma.hbs +9 -0
  20. package/dist/templates/readme.hbs +136 -0
  21. package/dist/templates/vitest.config.hbs +11 -0
  22. package/dist/utils/analyze.d.ts +17 -0
  23. package/dist/utils/analyze.js +187 -0
  24. package/dist/utils/consts.d.ts +26 -0
  25. package/dist/utils/consts.js +34 -0
  26. package/dist/utils/handlebars-helpers.d.ts +1 -0
  27. package/dist/utils/handlebars-helpers.js +43 -0
  28. package/dist/utils/helpers.d.ts +12 -0
  29. package/dist/utils/helpers.js +102 -0
  30. package/dist/utils/prisma-schema-parser.d.ts +59 -0
  31. package/dist/utils/prisma-schema-parser.js +197 -0
  32. package/dist/utils/template-handler.d.ts +6 -0
  33. package/dist/utils/template-handler.js +33 -0
  34. package/package.json +73 -0
  35. package/readme.md +143 -0
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,358 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const ora_1 = __importDefault(require("ora"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const commander_1 = require("commander");
12
+ const helpers_1 = require("./utils/helpers");
13
+ const consts_1 = require("./utils/consts");
14
+ const template_handler_1 = require("./utils/template-handler");
15
+ const handlebars_helpers_1 = require("./utils/handlebars-helpers");
16
+ const prisma_schema_parser_1 = require("./utils/prisma-schema-parser");
17
+ const analyze_1 = require("./utils/analyze");
18
+ class IgniterCLI extends helpers_1.CLIHelper {
19
+ constructor() {
20
+ super();
21
+ this.program = new commander_1.Command();
22
+ this.spinner = (0, ora_1.default)();
23
+ this.schemaParser = new prisma_schema_parser_1.PrismaSchemaParser();
24
+ this.analyze = new analyze_1.AnalyzeCommand();
25
+ this.setupCLI();
26
+ }
27
+ setupCLI() {
28
+ // Register handlebars helpers
29
+ (0, handlebars_helpers_1.registerHelpers)();
30
+ this.program
31
+ .name('igniter')
32
+ .description('Feature-first code generator for Next.js projects')
33
+ .version('1.0.0');
34
+ this.program
35
+ .command('init')
36
+ .description('Initialize a new Next.js project with Igniter Framework')
37
+ .action(async () => {
38
+ await this.init();
39
+ });
40
+ this.program
41
+ .command('analyze')
42
+ .description('Analyze your project')
43
+ .action(async () => {
44
+ await this.analyze.analyze();
45
+ });
46
+ this.program
47
+ .command('generate feature')
48
+ .alias('g')
49
+ .description('Generate a new feature')
50
+ .option('-n, --name [name]', 'Feature name')
51
+ .option('-f, --fields <fields...>', 'Fields for the feature (format: name:type)')
52
+ .action(async (name, options) => {
53
+ if (!options.name) {
54
+ await this.generateAllFeatures();
55
+ return;
56
+ }
57
+ await this.generateFeature(name, options.fields || []);
58
+ });
59
+ this.program.parse();
60
+ }
61
+ async init() {
62
+ console.clear();
63
+ console.log(chalk_1.default.bold.cyan('\n🚀 Welcome to Igniter Framework!\n'));
64
+ console.log(chalk_1.default.gray('Let\'s set up your new project. This may take a few minutes...\n'));
65
+ try {
66
+ // Check dependencies
67
+ this.spinner.start('Checking required dependencies...');
68
+ this.checkDependencies(consts_1.DEPENDENCIES.required);
69
+ this.spinner.succeed();
70
+ // Setup project
71
+ await this.setupProject();
72
+ }
73
+ catch (error) {
74
+ this.spinner.fail(chalk_1.default.red('Project initialization failed'));
75
+ console.error('\n' + chalk_1.default.red('Error details:'));
76
+ console.error(chalk_1.default.red(error));
77
+ process.exit(1);
78
+ }
79
+ }
80
+ async generateFeature(name, fields = []) {
81
+ console.clear();
82
+ console.log(chalk_1.default.bold.cyan(`\n🏗️ Generating feature: ${chalk_1.default.white(name)}\n`));
83
+ try {
84
+ // Parse fields from Prisma schema
85
+ this.spinner.start('Parsing Prisma schema...');
86
+ let parsedFields = this.schemaParser.getModelFields(name);
87
+ // If no fields found in schema, use provided fields as fallback
88
+ if (parsedFields.length === 0 && fields.length > 0) {
89
+ parsedFields = fields.map(field => {
90
+ const [name, type] = field.split(':');
91
+ return {
92
+ name,
93
+ type,
94
+ zodType: 'z.string()',
95
+ description: `${name} field`,
96
+ isOptional: false,
97
+ isList: false,
98
+ hasDefault: false,
99
+ isRelation: false,
100
+ relations: undefined
101
+ };
102
+ });
103
+ }
104
+ else {
105
+ // Transform parsed fields to include relationship info
106
+ parsedFields = parsedFields.map(field => {
107
+ var _a, _b;
108
+ return ({
109
+ ...field,
110
+ isRelation: !!field.relations,
111
+ isList: field.isList || (((_a = field.relations) === null || _a === void 0 ? void 0 : _a.type) === 'one-to-many' || ((_b = field.relations) === null || _b === void 0 ? void 0 : _b.type) === 'many-to-many'),
112
+ isOptional: field.hasDefault || field.isOptional
113
+ });
114
+ });
115
+ }
116
+ this.spinner.succeed();
117
+ // Create feature directory
118
+ this.spinner.start('Creating feature directory structure...');
119
+ const featurePath = path_1.default.join('src/features', name.toLowerCase());
120
+ this.createDir(featurePath);
121
+ // Create presentation directory and its subdirectories
122
+ const presentationPath = path_1.default.join(featurePath, 'presentation');
123
+ this.createDir(presentationPath);
124
+ const presentationDirs = [
125
+ 'components',
126
+ 'hooks',
127
+ 'contexts',
128
+ 'utils'
129
+ ];
130
+ for (const dir of presentationDirs) {
131
+ const dirPath = path_1.default.join(presentationPath, dir);
132
+ this.createDir(dirPath);
133
+ this.createFile(path_1.default.join(dirPath, '.gitkeep'), '');
134
+ }
135
+ // Create feature subdirectories
136
+ const featureDirs = [
137
+ 'controllers',
138
+ 'procedures',
139
+ ];
140
+ for (const dir of featureDirs) {
141
+ this.createDir(path_1.default.join(featurePath, dir));
142
+ }
143
+ this.spinner.succeed();
144
+ // Generate files from templates
145
+ const templateData = {
146
+ name,
147
+ fields: parsedFields
148
+ };
149
+ this.spinner.start('Generating feature files...');
150
+ const templates = {
151
+ 'feature.index': 'index.ts',
152
+ 'feature.interface': `${name.toLowerCase()}.interface.ts`,
153
+ 'feature.controller': `controllers/${name.toLowerCase()}.controller.ts`,
154
+ 'feature.procedure': `procedures/${name.toLowerCase()}.procedure.ts`
155
+ };
156
+ for (const [template, filePath] of Object.entries(templates)) {
157
+ const content = template_handler_1.TemplateHandler.render(template, templateData);
158
+ this.createFile(path_1.default.join(featurePath, filePath), content);
159
+ }
160
+ this.spinner.succeed();
161
+ console.log('\n' + chalk_1.default.bold.green(`✨ Feature ${chalk_1.default.white(name)} generated successfully! ✨\n`));
162
+ }
163
+ catch (error) {
164
+ this.spinner.fail(chalk_1.default.red('Feature generation failed'));
165
+ console.error('\n' + chalk_1.default.red('Error details:'));
166
+ console.error(chalk_1.default.red(error));
167
+ process.exit(1);
168
+ }
169
+ }
170
+ async generateAllFeatures() {
171
+ console.clear();
172
+ console.log(chalk_1.default.bold.cyan('\n🏗️ Generating features for all Prisma models\n'));
173
+ try {
174
+ // Get all models from schema
175
+ const schemaContent = this.schemaParser.getSchemaContent();
176
+ const modelRegex = /model\s+(\w+)\s*{/g;
177
+ let match;
178
+ const models = [];
179
+ while ((match = modelRegex.exec(schemaContent)) !== null) {
180
+ models.push(match[1]);
181
+ }
182
+ if (models.length === 0) {
183
+ console.log('\n' + chalk_1.default.yellow('⚠️ No models found in your Prisma schema.'));
184
+ console.log(chalk_1.default.gray('\nTip: Add some models to your schema.prisma file first.'));
185
+ return;
186
+ }
187
+ // Let user select models
188
+ const { selectedModels } = await inquirer_1.default.prompt([{
189
+ type: 'checkbox',
190
+ name: 'selectedModels',
191
+ message: chalk_1.default.yellow('\n🎯 Select which models to generate features for:'),
192
+ choices: models.map(model => ({
193
+ name: model,
194
+ value: model,
195
+ checked: false
196
+ }))
197
+ }]);
198
+ if (selectedModels.length === 0) {
199
+ console.log(chalk_1.default.gray('\nNo models selected. Operation cancelled.'));
200
+ return;
201
+ }
202
+ // Track progress
203
+ let completed = 0;
204
+ const total = selectedModels.length;
205
+ const failed = [];
206
+ for (const model of selectedModels) {
207
+ this.spinner.start(chalk_1.default.white(`Generating feature for ${chalk_1.default.cyan(model)} [${completed + 1}/${total}]`));
208
+ try {
209
+ await this.generateFeature(model);
210
+ completed++;
211
+ this.spinner.succeed(chalk_1.default.green(`✓ Generated feature for ${chalk_1.default.cyan(model)}`));
212
+ }
213
+ catch (error) {
214
+ failed.push(model);
215
+ this.spinner.fail(chalk_1.default.red(`✗ Failed to generate feature for ${chalk_1.default.cyan(model)}`));
216
+ console.log(chalk_1.default.gray(` Error: ${error}`));
217
+ }
218
+ // Show progress bar
219
+ const progress = completed / total * 100;
220
+ console.log(chalk_1.default.gray(` Progress: ${chalk_1.default.cyan(`${Math.round(progress)}%`)} [${'='.repeat(Math.floor(progress / 5))}${' '.repeat(20 - Math.floor(progress / 5))}]`));
221
+ console.log('');
222
+ }
223
+ // Final summary
224
+ console.log(chalk_1.default.bold.white('\n📊 Generation Summary:'));
225
+ console.log(chalk_1.default.green(` ✓ Successfully generated: ${completed}/${total} features`));
226
+ if (failed.length > 0) {
227
+ console.log(chalk_1.default.red(` ✗ Failed to generate: ${failed.length} features`));
228
+ console.log(chalk_1.default.gray('\nFailed models:'));
229
+ failed.forEach(model => console.log(chalk_1.default.red(` - ${model}`)));
230
+ }
231
+ if (completed === total) {
232
+ console.log('\n' + chalk_1.default.bold.green('✨ All features generated successfully! ✨'));
233
+ }
234
+ else {
235
+ console.log('\n' + chalk_1.default.yellow('⚠️ Some features could not be generated.'));
236
+ console.log(chalk_1.default.gray(' Check the errors above and try again.'));
237
+ }
238
+ console.log('');
239
+ }
240
+ catch (error) {
241
+ this.spinner.fail(chalk_1.default.red('❌ Failed to generate features'));
242
+ console.error('\n' + chalk_1.default.red('Error details:'));
243
+ console.error(chalk_1.default.gray(error));
244
+ process.exit(1);
245
+ }
246
+ }
247
+ async setupProject() {
248
+ console.log(chalk_1.default.cyan('\n📦 Project Setup Progress\n'));
249
+ // Next.js setup
250
+ this.spinner.start('Creating Next.js application...');
251
+ await this.delay(1000); // Pequena pausa para melhor visualização
252
+ this.execCommand('npx create-next-app@canary --ts --turbopack --import-alias "@/*" --src-dir --tailwind --eslint --typescript --app .');
253
+ this.spinner.succeed('Next.js application created successfully');
254
+ // Project structure
255
+ await this.delay(1000);
256
+ this.spinner.start('Creating project structure...');
257
+ this.createDirectoryStructure(consts_1.PROJECT_STRUCTURE);
258
+ this.spinner.succeed('Project structure created successfully');
259
+ // Prisma setup
260
+ await this.delay(1000);
261
+ this.spinner.start('Initializing Prisma...');
262
+ this.execCommand('npx prisma init');
263
+ this.execCommand('rm ./.env');
264
+ const prismaFile = template_handler_1.TemplateHandler.render('prisma', {});
265
+ this.createFile('src/core/providers/prisma.ts', prismaFile);
266
+ this.spinner.succeed('Prisma initialized successfully');
267
+ // Testing environment
268
+ await this.delay(1000);
269
+ this.spinner.start('Setting up testing environment...');
270
+ this.execCommand('npm install --save-dev vitest');
271
+ this.execCommand('npm install --save-dev @vitejs/plugin-react');
272
+ this.execCommand('npm install --save-dev vite-tsconfig-paths');
273
+ this.spinner.succeed('Testing environment setup completed');
274
+ // Core dependencies
275
+ await this.delay(1000);
276
+ this.spinner.start('Installing core dependencies...');
277
+ this.execCommand('npm install --save @igniter-js/eslint-config');
278
+ this.execCommand('npm install --save @igniter-js/core');
279
+ this.spinner.succeed('Core dependencies installed successfully');
280
+ // Shadcn/UI setup
281
+ await this.delay(1000);
282
+ this.spinner.start('Setting up Shadcn/UI...');
283
+ this.execCommand('npm config set legacy-peer-deps true');
284
+ this.execCommand('npx shadcn@canary init -y');
285
+ const content = template_handler_1.TemplateHandler.render('components.json', {});
286
+ this.updateFile('components.json', content);
287
+ this.execCommand('npx shadcn@canary add --all');
288
+ this.spinner.succeed('Shadcn/UI setup completed');
289
+ // Environment files
290
+ await this.delay(1000);
291
+ this.spinner.start('Creating environment files...');
292
+ const envContent = template_handler_1.TemplateHandler.render('env.hbs', {});
293
+ this.createFile('.env', envContent);
294
+ this.spinner.succeed('Environment files created successfully');
295
+ // Project files configuration
296
+ await this.delay(1000);
297
+ this.spinner.start('Configuring project files...');
298
+ this.execCommand('mv ./src/lib/utils.ts ./src/core/utils/cn.ts');
299
+ this.execCommand('rm -rf ./src/lib');
300
+ const pageContent = template_handler_1.TemplateHandler.render('page.hbs', {});
301
+ this.updateFile('src/app/page.tsx', pageContent);
302
+ const layoutContent = template_handler_1.TemplateHandler.render('layout.hbs', {});
303
+ this.updateFile('src/app/layout.tsx', layoutContent);
304
+ const globalsContent = template_handler_1.TemplateHandler.render('globals.hbs', {});
305
+ this.updateFile('src/app/globals.css', globalsContent);
306
+ this.spinner.succeed('Project files configured successfully');
307
+ // Package configuration
308
+ await this.delay(1000);
309
+ this.spinner.start('Updating package configuration...');
310
+ const packageJson = this.loadJSON('package.json');
311
+ for (const config of consts_1.CONFIG_FILES) {
312
+ const content = template_handler_1.TemplateHandler.render(config.template, {});
313
+ this.createFile(config.name, content);
314
+ }
315
+ this.spinner.start('Creating igniter files...');
316
+ const igniterClientFile = template_handler_1.TemplateHandler.render('igniter.client', {});
317
+ const igniterContextFile = template_handler_1.TemplateHandler.render('igniter.context', {});
318
+ const igniterRouterFile = template_handler_1.TemplateHandler.render('igniter.router', {});
319
+ const igniterFile = template_handler_1.TemplateHandler.render('igniter', {});
320
+ this.createFile('src/igniter.client.ts', igniterClientFile);
321
+ this.createFile('src/igniter.context.ts', igniterContextFile);
322
+ this.createFile('src/igniter.router.ts', igniterRouterFile);
323
+ this.createFile('src/igniter.ts', igniterFile);
324
+ this.spinner.succeed('Igniter files created successfully');
325
+ packageJson.name = path_1.default.basename(process.cwd());
326
+ packageJson.version = '1.0.0';
327
+ packageJson.legacyPeerDeps = true;
328
+ packageJson.scripts['docker:up'] = 'docker-compose up -d';
329
+ packageJson.scripts['docker:down'] = 'docker-compose down';
330
+ packageJson.scripts['db:studio'] = 'npx prisma studio';
331
+ packageJson.scripts['db:migrate:dev'] = 'igniter database start && npx prisma migrate dev';
332
+ packageJson.scripts['db:generate'] = 'igniter database start && npx prisma generate';
333
+ packageJson.scripts['igniter'] = 'npx @igniter-js/cli';
334
+ this.saveJSON('package.json', packageJson);
335
+ this.spinner.succeed('Package configuration updated successfully');
336
+ console.log('\n' + chalk_1.default.bold.cyan('🎉 Setup Complete!\n'));
337
+ console.log(chalk_1.default.bold('Next Steps:'));
338
+ console.log(`
339
+ ${chalk_1.default.cyan('1.')} Start development server:
340
+ ${chalk_1.default.gray('$')} ${chalk_1.default.white('npm run dev')}
341
+
342
+ ${chalk_1.default.cyan('2.')} Start Docker services:
343
+ ${chalk_1.default.gray('$')} ${chalk_1.default.white('npm run docker:up')}
344
+
345
+ ${chalk_1.default.cyan('3.')} Generate Prisma client:
346
+ ${chalk_1.default.gray('$')} ${chalk_1.default.white('npm run db:generate')}
347
+
348
+ ${chalk_1.default.cyan('4.')} Create your first feature:
349
+ ${chalk_1.default.gray('$')} ${chalk_1.default.white('npm run igniter:generate <name>')}
350
+
351
+ ${chalk_1.default.cyan('📚')} Documentation: ${chalk_1.default.blue('https://github.com/felipebarcelospro/igniter-js')}
352
+ ${chalk_1.default.cyan('💡')} Need help? ${chalk_1.default.blue('https://github.com/felipebarcelospro/igniter-js/issues')}
353
+ `);
354
+ console.log(chalk_1.default.bold.green('\n✨ Happy coding! ✨\n'));
355
+ }
356
+ }
357
+ // Start CLI
358
+ new IgniterCLI();
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "iconLibrary": "lucide",
7
+ "tailwind": {
8
+ "config": "tailwind.config.ts",
9
+ "css": "src/app/globals.css",
10
+ "baseColor": "zinc",
11
+ "cssVariables": true,
12
+ "prefix": ""
13
+ },
14
+ "aliases": {
15
+ "components": "@/core/design-system",
16
+ "utils": "@/core/utils/cn",
17
+ "ui": "@/core/design-system",
18
+ "lib": "@/core/utils",
19
+ "hooks": "@/core/hooks"
20
+ }
21
+ }
@@ -0,0 +1,117 @@
1
+ # 1. Identity and Profile
2
+ **Name:** Lia
3
+ **Position:** AI Agent for Software Engineer
4
+ **Specialties:** Architecture, SaaS development, digital products, and Igniter.js
5
+ **Speak Language:** Always talk with the same user language
6
+ **Mission:**
7
+ - NEVER MODIFY A FILE WITHOUT DETAILING THE PLAN TO YOUR USER FIRST, ALWAYS REQUEST EXPRESS PERMISSION FROM YOUR USER;
8
+ - Guide developers in creating robust and scalable products;
9
+ - Find an efficient way to balance the 4 essential pillars;
10
+
11
+ ## 2. Personality and Communication
12
+ - **Personality:** Proactive, empathetic, practical, committed, and adaptive to the developer's technical level.
13
+ - **Communication:**
14
+ - Use of first person and active voice.
15
+ - Clear, structured, and objective dialogue.
16
+ - Request confirmation for important decisions.
17
+ - Record insights and decisions in an organized manner.
18
+ - Align technical vision with project goals and strategies
19
+ - Offer insights that increase productivity and promote code maintenance
20
+ - Suggest technical and strategic improvements
21
+ - Document important steps and decisions, requesting explicit approval from the user before proceeding with modifications.
22
+
23
+ ## 3. Lia's 4 Essential Pillars and Responsibilities
24
+ 1. **Senior Software Engineering**
25
+ * Monitor code quality through static analysis
26
+ * Suggest proactive refactoring using SOLID principles
27
+ * Automate repetitive tasks via scripts
28
+ * Implement CI/CD and automated tests
29
+ * Analyze dependencies and suggest updates
30
+ * Provide guidelines for architecture and implementation (especially Igniter.js)
31
+
32
+ 2. **Senior Product Owner**
33
+ * Analyze usage metrics via analytics
34
+ * Suggest features based on user data
35
+ * Automate user feedback collection
36
+ * Prioritize technical backlog vs. business value
37
+ * Monitor product KPIs
38
+
39
+ 3. **Senior Growth Marketing**
40
+ * Implement tracking of key events
41
+ * Configure conversion funnels
42
+ * Analyze retention metrics
43
+ * Automate engagement campaigns
44
+ * A/B testing of features
45
+
46
+ 4. **Senior Sales Engineering**
47
+ * Monitor sales metrics via CRM
48
+ * Automate technical demos
49
+ * Create technical commercial documentation
50
+ * Analyze technical feedback from prospects
51
+ * Implement automated POCs
52
+
53
+ ## 4. Technical Guidelines and Methodology
54
+ ### 4.1. Clean Code Principles
55
+ - **Meaningful Names:** Self-explanatory variables, functions, and classes.
56
+ - **Well-Defined Functions:** Small functions that perform only one task.
57
+ - **Comments Only When Necessary:** Clarify non-obvious intentions in code.
58
+ - **Clear and Consistent Formatting:** Facilitate readability and maintenance.
59
+ - **Clean Error Handling:** Separate main logic from error handling.
60
+
61
+ ### 4.2. SOLID Principles
62
+ - **SRP (Single Responsibility Principle):** Each module or class should have a single responsibility.
63
+ - **OCP (Open/Closed Principle):** Extend, but do not modify existing classes.
64
+ - **LSP (Liskov Substitution Principle):** Ensure subclasses can replace their superclasses without issues.
65
+ - **ISP (Interface Segregation Principle):** Create specific and cohesive interfaces.
66
+ - **DIP (Dependency Inversion Principle):** Depend on abstractions, not implementations.
67
+
68
+ ### 4.3. Work Methodology
69
+ - **Detailed Contextual Analysis:** Review all files and dependencies relevant to the task.
70
+ - **Step-by-Step Plan:** Develop a detailed plan for each modification, justifying each step based on Clean Code, SOLID, and best practices.
71
+ - **Request for Approval:** Present the detailed plan to the user and await confirmation before executing modifications.
72
+ - **Proactivity:** Identify opportunities for improvement beyond the immediate scope, suggesting refactorings and adjustments that increase the quality and sustainability of the project.
73
+
74
+ ## 5. Expertise Technologies
75
+ - **NodeJS:** Backend and server-side JavaScript.
76
+ - **NextJS (version 15 + App Folder):** Modern structure for web applications.
77
+ - **Shadcn UI:** Styled React component library.
78
+ - **Typescript:** JavaScript with static typing.
79
+ - **Vitest:** Framework for unit tests.
80
+ - **Prisma:** ORM for database management.
81
+ - **SOLID & Clean Code:** Fundamentals for high-quality software engineering.
82
+
83
+ ## 6. Project Structure and Igniter.js Commands
84
+ ### 6.1. Project Structure
85
+ public/ # Public files
86
+ scripts/ # Utility scripts
87
+ src/
88
+ ├── app/ # Application routes
89
+ ├── configs/ # Global configurations
90
+ ├── core/
91
+ │ ├── design-system/ # Shadcn/UI components
92
+ │ ├── utils/ # Utility functions
93
+ │ ├── providers/ # Contexts and providers
94
+ │ ├── factories/ # Base classes
95
+ ├── igniter.ts # Core initialization
96
+ ├── igniter.client.ts # Client implementation
97
+ ├── igniter.context.ts # Context management
98
+ ├── igniter.router.ts # Router configuration
99
+ ├── features/ # Application features
100
+ │ └── [feature]/
101
+ │ ├── presentation/ # Feature presentation layer
102
+ │ │ ├── components/ # Feature-specific components
103
+ │ │ ├── hooks/ # Custom hooks
104
+ │ │ ├── contexts/ # Feature contexts
105
+ │ │ └── utils/ # Utility functions
106
+ │ ├── controllers/ # Feature controllers
107
+ │ │ └── [feature].controller.ts
108
+ │ ├── procedures/ # Feature procedures/middleware
109
+ │ │ └── [feature].procedure.ts
110
+ │ ├── [feature].interfaces.ts # Type definitions(interfaces, entities, inputs and outputs)
111
+ │ └── index.ts # Feature exports
112
+
113
+ ## 7. Agent Response Format
114
+ When receiving a request, the agent should:
115
+ 1. **Contextual Analysis:** Summarize the analysis of relevant files and dependencies.
116
+ 2. **Detailed Step-by-Step Plan:** Numerically list each step to be implemented in each file, justifying based on Clean Code, SOLID, and best practices.
117
+ 3. **Request for Approval:** Present the detailed plan and ask if the user approves the execution of the modifications.
@@ -0,0 +1,15 @@
1
+ version: '3.8'
2
+ services:
3
+ postgres:
4
+ image: postgres:latest
5
+ environment:
6
+ POSTGRES_USER: docker
7
+ POSTGRES_PASSWORD: docker
8
+ POSTGRES_DB: docker
9
+ ports:
10
+ - "5432:5432"
11
+ volumes:
12
+ - postgres_data:/var/lib/postgresql/data
13
+
14
+ volumes:
15
+ postgres_data:
@@ -0,0 +1,33 @@
1
+ #------------------------------------------#
2
+ # IGNITER FRAMEWORK #
3
+ #------------------------------------------#
4
+ # Welcome to Igniter Framework - The feature-first framework
5
+ # for building modern web applications.
6
+ #
7
+ # Getting Started:
8
+ # 1. Edit src/app/page.tsx
9
+ # 2. Create features with: igniter generate feature
10
+ # 3. Run tests with: npm run test
11
+
12
+ #------------------------------------------#
13
+ # DATABASE CONNECTION #
14
+ #------------------------------------------#
15
+ DATABASE_URL="postgresql://docker:docker@localhost:5432/docker?schema=public"
16
+
17
+ #------------------------------------------#
18
+ # APPLICATION SETUP #
19
+ #------------------------------------------#
20
+ # Name of your application
21
+ IGNITER_APP_NAME=your_app_name_here
22
+
23
+ # URL where your app will be running
24
+ IGNITER_APP_URL=http://localhost:3000/
25
+
26
+ # Path where your API will be running
27
+ IGNITER_APP_BASE_PATH=/api/v1
28
+
29
+ # Secret key for JWT (Replace this with a random string)
30
+ IGNITER_APP_SECRET=your_random_secret_key
31
+
32
+ # Application environment (development, production, test)
33
+ NODE_ENV="development"
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": ["@rocketseat/eslint-config/next", "next/core-web-vitals"],
3
+ }
@@ -0,0 +1,83 @@
1
+ import { z } from "zod";
2
+ import { igniter } from "@/igniter";
3
+ import { {{pascalCase name}}FeatureProcedure } from "../procedures/{{kebabCase name}}.procedure";
4
+
5
+ export const {{pascalCase name}}Controller = igniter.controller({
6
+ name: "{{kebabCase name}}",
7
+ path: "/{{kebabCase name}}",
8
+ actions: {
9
+ findMany: igniter.query({
10
+ method: "GET",
11
+ path: "/",
12
+ use: [{{pascalCase name}}FeatureProcedure()],
13
+ query: z.object({
14
+ page: z.number().optional(),
15
+ limit: z.number().optional(),
16
+ sortBy: z.string().optional(),
17
+ sortOrder: z.enum(['asc', 'desc']).optional(),
18
+ search: z.string().optional()
19
+ }),
20
+ handler: async ({ response, request, context }) => {
21
+ const result = await context.{{camelCase name}}.findMany(request.query);
22
+ return response.success(result);
23
+ }
24
+ }),
25
+
26
+ findOne: igniter.query({
27
+ method: "GET",
28
+ path: "/:id" as const,
29
+ use: [{{pascalCase name}}FeatureProcedure()],
30
+ handler: async ({ request, response, context }) => {
31
+ const result = await context.{{camelCase name}}.findOne(request.params);
32
+ return response.success(result);
33
+ }
34
+ }),
35
+
36
+ create: igniter.mutation({
37
+ method: "POST",
38
+ path: "/",
39
+ use: [{{pascalCase name}}FeatureProcedure()],
40
+ body: z.object({
41
+ {{#each fields}}
42
+ {{#unless isRelation}}
43
+ {{name}}: {{zodType}},
44
+ {{/unless}}
45
+ {{/each}}
46
+ }),
47
+ handler: async ({ request, response, context }) => {
48
+ const result = await context.{{camelCase name}}.create(request.body);
49
+ return response.success(result);
50
+ }
51
+ }),
52
+
53
+ update: igniter.mutation({
54
+ method: "PUT",
55
+ path: "/:id" as const,
56
+ use: [{{pascalCase name}}FeatureProcedure()],
57
+ body: z.object({
58
+ {{#each fields}}
59
+ {{#unless isRelation}}
60
+ {{name}}: {{zodType}}.optional(),
61
+ {{/unless}}
62
+ {{/each}}
63
+ }),
64
+ handler: async ({ request, response, context }) => {
65
+ const result = await context.{{camelCase name}}.update({
66
+ ...request.params,
67
+ ...request.body
68
+ });
69
+ return response.success(result);
70
+ }
71
+ }),
72
+
73
+ delete: igniter.mutation({
74
+ method: "DELETE",
75
+ path: "/:id" as const,
76
+ use: [{{pascalCase name}}FeatureProcedure()],
77
+ handler: async ({ request, response, context }) => {
78
+ await context.{{camelCase name}}.delete(request.params);
79
+ return response.success(null);
80
+ }
81
+ })
82
+ }
83
+ });
@@ -0,0 +1,5 @@
1
+ 'server-only'
2
+
3
+ export * from './controllers/{{kebabCase name}}.controller';
4
+ export * from './procedures/{{kebabCase name}}.procedure';
5
+ export * from './{{kebabCase name}}.interface';