@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.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +358 -0
- package/dist/templates/components.json.hbs +21 -0
- package/dist/templates/copilot.instructions.hbs +117 -0
- package/dist/templates/docker-compose.hbs +15 -0
- package/dist/templates/env.hbs +33 -0
- package/dist/templates/eslintrc.hbs +3 -0
- package/dist/templates/feature.controller.hbs +83 -0
- package/dist/templates/feature.index.hbs +5 -0
- package/dist/templates/feature.interface.hbs +71 -0
- package/dist/templates/feature.procedure.hbs +76 -0
- package/dist/templates/globals.hbs +198 -0
- package/dist/templates/igniter.client.hbs +21 -0
- package/dist/templates/igniter.context.hbs +23 -0
- package/dist/templates/igniter.hbs +8 -0
- package/dist/templates/igniter.router.hbs +29 -0
- package/dist/templates/layout.hbs +54 -0
- package/dist/templates/page.hbs +313 -0
- package/dist/templates/prisma.hbs +9 -0
- package/dist/templates/readme.hbs +136 -0
- package/dist/templates/vitest.config.hbs +11 -0
- package/dist/utils/analyze.d.ts +17 -0
- package/dist/utils/analyze.js +187 -0
- package/dist/utils/consts.d.ts +26 -0
- package/dist/utils/consts.js +34 -0
- package/dist/utils/handlebars-helpers.d.ts +1 -0
- package/dist/utils/handlebars-helpers.js +43 -0
- package/dist/utils/helpers.d.ts +12 -0
- package/dist/utils/helpers.js +102 -0
- package/dist/utils/prisma-schema-parser.d.ts +59 -0
- package/dist/utils/prisma-schema-parser.js +197 -0
- package/dist/utils/template-handler.d.ts +6 -0
- package/dist/utils/template-handler.js +33 -0
- package/package.json +73 -0
- package/readme.md +143 -0
package/dist/index.d.ts
ADDED
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,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
|
+
});
|