@node-initializr/generator 0.1.0 → 0.1.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/dist/ai-config.d.ts +4 -0
- package/dist/ai-config.js +217 -0
- package/dist/generate.js +4 -1
- package/dist/validate.js +6 -0
- package/package.json +3 -3
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type ProjectConfig } from '@node-initializr/shared';
|
|
2
|
+
import type { GeneratedFile } from './template-engine.js';
|
|
3
|
+
export declare function buildAiGuidelines(config: ProjectConfig): string;
|
|
4
|
+
export declare function renderAiConfigFiles(config: ProjectConfig): GeneratedFile[];
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildAiGuidelines = buildAiGuidelines;
|
|
4
|
+
exports.renderAiConfigFiles = renderAiConfigFiles;
|
|
5
|
+
const shared_1 = require("@node-initializr/shared");
|
|
6
|
+
function labelFor(options, value) {
|
|
7
|
+
return options.find((o) => o.value === value)?.label ?? value;
|
|
8
|
+
}
|
|
9
|
+
function pmRun(config, script) {
|
|
10
|
+
if (config.packageManager === 'pnpm')
|
|
11
|
+
return `pnpm run ${script}`;
|
|
12
|
+
if (config.packageManager === 'yarn')
|
|
13
|
+
return `yarn ${script}`;
|
|
14
|
+
return `npm run ${script}`;
|
|
15
|
+
}
|
|
16
|
+
function pmExec(config, script) {
|
|
17
|
+
if (config.packageManager === 'pnpm')
|
|
18
|
+
return `pnpm ${script}`;
|
|
19
|
+
if (config.packageManager === 'yarn')
|
|
20
|
+
return `yarn ${script}`;
|
|
21
|
+
return `npm run ${script}`;
|
|
22
|
+
}
|
|
23
|
+
function architectureGuidelines(config) {
|
|
24
|
+
if (config.architecture === 'clean') {
|
|
25
|
+
return `## Arquitetura — Clean Architecture
|
|
26
|
+
|
|
27
|
+
- **domain/** — entidades e interfaces de repositório (sem dependências externas)
|
|
28
|
+
- **application/** — casos de uso que orquestram a lógica de negócio
|
|
29
|
+
- **infrastructure/** — implementações concretas (HTTP, persistência, mensageria)
|
|
30
|
+
- **shared/** — erros e utilitários compartilhados
|
|
31
|
+
|
|
32
|
+
Regras:
|
|
33
|
+
- Domínio não importa de infrastructure nem de frameworks HTTP
|
|
34
|
+
- Casos de uso dependem de interfaces do domínio, não de implementações
|
|
35
|
+
- Controllers/rotas apenas adaptam HTTP → casos de uso`;
|
|
36
|
+
}
|
|
37
|
+
if (config.architecture === 'mvc') {
|
|
38
|
+
return `## Arquitetura — MVC
|
|
39
|
+
|
|
40
|
+
- **controllers/** — entrada HTTP, validação de request/response
|
|
41
|
+
- **services/** — regras de negócio e orquestração
|
|
42
|
+
- **models/** — estruturas de dados e entidades
|
|
43
|
+
|
|
44
|
+
Regras:
|
|
45
|
+
- Controllers devem ser finos — delegam lógica para services
|
|
46
|
+
- Services não conhecem detalhes HTTP (req/res)
|
|
47
|
+
- Models representam dados, não lógica de transporte`;
|
|
48
|
+
}
|
|
49
|
+
return `## Arquitetura — Modular Monolith
|
|
50
|
+
|
|
51
|
+
- **modules/** — um módulo por domínio (ex: users, health)
|
|
52
|
+
- Cada módulo contém controller, service e module (NestJS) ou routes equivalentes
|
|
53
|
+
- **shared/** ou **infra/** — código transversal (DB, auth, config)
|
|
54
|
+
|
|
55
|
+
Regras:
|
|
56
|
+
- Módulos não importam implementações internas de outros módulos diretamente
|
|
57
|
+
- Comunicação entre módulos via interfaces públicas ou eventos
|
|
58
|
+
- Mantenha boundaries claros para facilitar extração futura em microserviços`;
|
|
59
|
+
}
|
|
60
|
+
function frameworkGuidelines(config) {
|
|
61
|
+
if (config.framework === 'nestjs') {
|
|
62
|
+
return `## Framework — NestJS
|
|
63
|
+
|
|
64
|
+
- Use módulos (\`@Module\`), injeção de dependência e providers
|
|
65
|
+
- Controllers com decorators (\`@Controller\`, \`@Get\`, etc.)
|
|
66
|
+
- Services como \`@Injectable()\`
|
|
67
|
+
- Prefira DTOs com class-validator para entrada
|
|
68
|
+
- Entry point: \`src/main.ts\``;
|
|
69
|
+
}
|
|
70
|
+
if (config.framework === 'express') {
|
|
71
|
+
return `## Framework — Express
|
|
72
|
+
|
|
73
|
+
- Rotas organizadas em módulos/arquivos separados
|
|
74
|
+
- Middleware para auth, parsing e erros
|
|
75
|
+
- Services puros importados pelos controllers/rotas
|
|
76
|
+
- Entry point: \`src/index.${config.language === 'typescript' ? 'ts' : 'js'}\``;
|
|
77
|
+
}
|
|
78
|
+
return `## Framework — Fastify
|
|
79
|
+
|
|
80
|
+
- Use plugins e \`register()\` para modularizar
|
|
81
|
+
- Schemas JSON para validação de request/response quando aplicável
|
|
82
|
+
- Hooks para auth e lifecycle
|
|
83
|
+
- Entry point: \`src/index.${config.language === 'typescript' ? 'ts' : 'js'}\``;
|
|
84
|
+
}
|
|
85
|
+
function stackSection(config) {
|
|
86
|
+
const lines = [
|
|
87
|
+
`- **Projeto:** ${config.name}`,
|
|
88
|
+
`- **Framework:** ${labelFor(shared_1.FRAMEWORK_OPTIONS, config.framework)}`,
|
|
89
|
+
`- **Linguagem:** ${labelFor(shared_1.LANGUAGE_OPTIONS, config.language)}`,
|
|
90
|
+
`- **Node.js:** ${config.nodeVersion}`,
|
|
91
|
+
`- **Package manager:** ${labelFor(shared_1.PACKAGE_MANAGER_OPTIONS, config.packageManager)}`,
|
|
92
|
+
`- **Arquitetura:** ${labelFor(shared_1.ARCHITECTURE_OPTIONS, config.architecture)}`,
|
|
93
|
+
`- **Banco:** ${labelFor(shared_1.DATABASE_OPTIONS, config.database)}`,
|
|
94
|
+
`- **ORM:** ${labelFor(shared_1.ORM_OPTIONS, config.orm)}`,
|
|
95
|
+
`- **Auth:** ${labelFor(shared_1.AUTH_OPTIONS, config.auth)}`,
|
|
96
|
+
`- **Mensageria:** ${labelFor(shared_1.MESSAGE_BROKER_OPTIONS, config.messageBroker)}`,
|
|
97
|
+
`- **Redis:** ${config.redis ? 'sim' : 'não'}`,
|
|
98
|
+
`- **Swagger:** ${config.swagger ? 'sim' : 'não'}`,
|
|
99
|
+
`- **Testes:** ${config.jest ? (config.framework === 'nestjs' ? 'Jest' : 'node:test') : 'não'}`,
|
|
100
|
+
`- **Docker:** ${config.docker ? 'sim' : 'não'}`,
|
|
101
|
+
`- **GitHub Actions:** ${config.githubActions ? 'sim' : 'não'}`,
|
|
102
|
+
];
|
|
103
|
+
if (config.description) {
|
|
104
|
+
lines.unshift(`- **Descrição:** ${config.description}`);
|
|
105
|
+
}
|
|
106
|
+
return lines.join('\n');
|
|
107
|
+
}
|
|
108
|
+
function dataLayerGuidelines(config) {
|
|
109
|
+
if (config.database === 'none' || config.orm === 'none') {
|
|
110
|
+
return `## Camada de dados
|
|
111
|
+
|
|
112
|
+
Este projeto não inclui banco de dados configurado. Não adicione ORM ou migrations sem alinhar com o time.`;
|
|
113
|
+
}
|
|
114
|
+
const ormNotes = {
|
|
115
|
+
prisma: '- Schema em `prisma/schema.prisma`\n- Use `PrismaClient` via service injetável\n- Migrations: `npx prisma migrate dev`',
|
|
116
|
+
typeorm: '- Entidades com decorators em `entities/`\n- DataSource configurado em `infra/typeorm/`\n- Use repositories ou EntityManager conforme o padrão do projeto',
|
|
117
|
+
drizzle: '- Schema em `infra/drizzle/schema`\n- Client tipado via Drizzle\n- Migrations com drizzle-kit',
|
|
118
|
+
sequelize: '- Models Sequelize em `infra/sequelize/models/`\n- Inicialização em `infra/sequelize/sequelize`\n- Use migrations do Sequelize para alterações de schema',
|
|
119
|
+
};
|
|
120
|
+
return `## Camada de dados
|
|
121
|
+
|
|
122
|
+
- Banco: **${labelFor(shared_1.DATABASE_OPTIONS, config.database)}**
|
|
123
|
+
- ORM: **${labelFor(shared_1.ORM_OPTIONS, config.orm)}**
|
|
124
|
+
|
|
125
|
+
${ormNotes[config.orm] ?? ''}`;
|
|
126
|
+
}
|
|
127
|
+
function buildAiGuidelines(config) {
|
|
128
|
+
const testCmd = config.framework === 'nestjs'
|
|
129
|
+
? pmRun(config, 'test')
|
|
130
|
+
: pmExec(config, 'test');
|
|
131
|
+
const devCmd = config.framework === 'nestjs'
|
|
132
|
+
? pmRun(config, 'start:dev')
|
|
133
|
+
: pmRun(config, 'dev');
|
|
134
|
+
return `# ${config.name} — instruções para assistentes de IA
|
|
135
|
+
|
|
136
|
+
Este arquivo foi gerado pelo [Node Initializr](https://github.com/pietro-sdev/node-initializr).
|
|
137
|
+
Siga estas diretrizes ao implementar funcionalidades neste projeto.
|
|
138
|
+
|
|
139
|
+
## Stack do projeto
|
|
140
|
+
|
|
141
|
+
${stackSection(config)}
|
|
142
|
+
|
|
143
|
+
${frameworkGuidelines(config)}
|
|
144
|
+
|
|
145
|
+
${architectureGuidelines(config)}
|
|
146
|
+
|
|
147
|
+
${dataLayerGuidelines(config)}
|
|
148
|
+
|
|
149
|
+
## Convenções gerais
|
|
150
|
+
|
|
151
|
+
- Linguagem: **${config.language === 'typescript' ? 'TypeScript estrito — evite `any`' : 'JavaScript (ESM)'}**
|
|
152
|
+
- Nomes de arquivos: kebab-case para pastas; siga o padrão já usado no módulo
|
|
153
|
+
- Novos endpoints: inclua health check compatível se alterar rotas globais
|
|
154
|
+
- Erros HTTP: respostas consistentes com o padrão existente no projeto
|
|
155
|
+
- Variáveis de ambiente: documente novas vars em \`.env.example\`
|
|
156
|
+
- Não reestruture a arquitetura escolhida sem solicitação explícita
|
|
157
|
+
|
|
158
|
+
## Comandos úteis
|
|
159
|
+
|
|
160
|
+
\`\`\`bash
|
|
161
|
+
${config.packageManager === 'pnpm' ? 'pnpm install' : config.packageManager === 'yarn' ? 'yarn install' : 'npm install'}
|
|
162
|
+
${devCmd}
|
|
163
|
+
${pmRun(config, 'build')}
|
|
164
|
+
${testCmd}
|
|
165
|
+
\`\`\`
|
|
166
|
+
|
|
167
|
+
## Ao adicionar features
|
|
168
|
+
|
|
169
|
+
1. Identifique o módulo/camada correto conforme a arquitetura **${labelFor(shared_1.ARCHITECTURE_OPTIONS, config.architecture)}**
|
|
170
|
+
2. Reutilize services, repositories e clients existentes
|
|
171
|
+
3. Mantenha compatibilidade com ${labelFor(shared_1.PACKAGE_MANAGER_OPTIONS, config.packageManager)} e Node ${config.nodeVersion}
|
|
172
|
+
4. Adicione ou atualize testes (${config.framework === 'nestjs' ? 'Jest' : 'node:test'}) quando alterar comportamento
|
|
173
|
+
${config.swagger ? '5. Documente endpoints novos no Swagger/OpenAPI' : ''}
|
|
174
|
+
`;
|
|
175
|
+
}
|
|
176
|
+
const AI_TOOL_FILES = {
|
|
177
|
+
agents: {
|
|
178
|
+
path: 'AGENTS.md',
|
|
179
|
+
wrap: (body) => body,
|
|
180
|
+
},
|
|
181
|
+
cursor: {
|
|
182
|
+
path: '.cursor/rules/node-initializr.mdc',
|
|
183
|
+
wrap: (body) => `---\ncontext: true\npriority: high\nscope: project\n---\n\n${body}`,
|
|
184
|
+
},
|
|
185
|
+
claude: {
|
|
186
|
+
path: '.claude/CLAUDE.md',
|
|
187
|
+
wrap: (body) => body,
|
|
188
|
+
},
|
|
189
|
+
copilot: {
|
|
190
|
+
path: '.github/copilot-instructions.md',
|
|
191
|
+
wrap: (body) => body,
|
|
192
|
+
},
|
|
193
|
+
gemini: {
|
|
194
|
+
path: '.gemini/GEMINI.md',
|
|
195
|
+
wrap: (body) => body,
|
|
196
|
+
},
|
|
197
|
+
jetbrains: {
|
|
198
|
+
path: '.junie/guidelines.md',
|
|
199
|
+
wrap: (body) => body,
|
|
200
|
+
},
|
|
201
|
+
windsurf: {
|
|
202
|
+
path: '.windsurf/rules/guidelines.md',
|
|
203
|
+
wrap: (body) => body,
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
function renderAiConfigFiles(config) {
|
|
207
|
+
if (!config.aiTools?.length)
|
|
208
|
+
return [];
|
|
209
|
+
const body = buildAiGuidelines(config);
|
|
210
|
+
return config.aiTools.map((tool) => {
|
|
211
|
+
const target = AI_TOOL_FILES[tool];
|
|
212
|
+
return {
|
|
213
|
+
path: target.path,
|
|
214
|
+
content: target.wrap(body),
|
|
215
|
+
};
|
|
216
|
+
});
|
|
217
|
+
}
|
package/dist/generate.js
CHANGED
|
@@ -40,6 +40,7 @@ exports.writeProject = writeProject;
|
|
|
40
40
|
const shared_1 = require("@node-initializr/shared");
|
|
41
41
|
const fs = __importStar(require("fs"));
|
|
42
42
|
const path = __importStar(require("path"));
|
|
43
|
+
const ai_config_js_1 = require("./ai-config.js");
|
|
43
44
|
const template_engine_js_1 = require("./template-engine.js");
|
|
44
45
|
const validate_js_1 = require("./validate.js");
|
|
45
46
|
const zip_js_1 = require("./zip.js");
|
|
@@ -56,7 +57,9 @@ function renderProject(config) {
|
|
|
56
57
|
if (errors.length > 0) {
|
|
57
58
|
throw new Error(errors.join('; '));
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
+
const projectFiles = engine.renderProject(config);
|
|
61
|
+
const aiFiles = (0, ai_config_js_1.renderAiConfigFiles)(config);
|
|
62
|
+
return [...projectFiles, ...aiFiles];
|
|
60
63
|
}
|
|
61
64
|
async function generateProject(config) {
|
|
62
65
|
const files = renderProject(config);
|
package/dist/validate.js
CHANGED
|
@@ -37,5 +37,11 @@ function validateProjectConfig(config) {
|
|
|
37
37
|
if (config.framework === 'nestjs' && config.language === 'javascript') {
|
|
38
38
|
errors.push('NestJS requer TypeScript.');
|
|
39
39
|
}
|
|
40
|
+
if (config.aiTools?.length) {
|
|
41
|
+
const invalid = config.aiTools.filter((tool) => !shared_1.AI_TOOL_VALUES.includes(tool));
|
|
42
|
+
if (invalid.length > 0) {
|
|
43
|
+
errors.push(`Ferramentas de IA inválidas: ${invalid.join(', ')}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
40
46
|
return errors;
|
|
41
47
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node-initializr/generator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Engine de geração de projetos Node.js para o Node Initializr",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "https://github.com/pietro-sdev/node-initializr.git",
|
|
8
|
+
"url": "git+https://github.com/pietro-sdev/node-initializr.git",
|
|
9
9
|
"directory": "packages/generator"
|
|
10
10
|
},
|
|
11
11
|
"publishConfig": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"prepack": "npm run build"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@node-initializr/shared": "
|
|
25
|
+
"@node-initializr/shared": "^0.1.2",
|
|
26
26
|
"archiver": "^7.0.1",
|
|
27
27
|
"handlebars": "^4.7.8"
|
|
28
28
|
},
|