@devstroupe/devkit-cli 1.1.16 → 1.2.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/boilerplates/angular-template/src/app/core/services/auth.service.ts +1 -1
- package/dist/boilerplates/angular-template/src/app/services/role.service.ts +1 -1
- package/dist/boilerplates/angular-template/src/app/services/user.service.ts +1 -1
- package/dist/boilerplates/nest-template/.dockerignore +8 -0
- package/dist/boilerplates/nest-template/package.json +13 -15
- package/dist/boilerplates/nest-template/src/app.module.ts +7 -10
- package/dist/boilerplates/nest-template/src/database/data-source.ts +4 -1
- package/dist/boilerplates/nest-template/src/database/migrations/1600000000000-create-roles.ts +2 -1
- package/dist/boilerplates/nest-template/src/database/migrations/1700000000000-create-users.ts +2 -2
- package/dist/boilerplates/nest-template/src/modules/auth/auth.controller.ts +1 -1
- package/dist/boilerplates/nest-template/src/modules/auth/auth.module.ts +2 -7
- package/dist/boilerplates/nest-template/src/modules/auth/auth.service.ts +13 -11
- package/dist/boilerplates/nest-template/src/modules/auth/jwt-auth.guard.ts +5 -6
- package/dist/boilerplates/nest-template/src/modules/core/core.module.ts +9 -0
- package/dist/boilerplates/nest-template/src/modules/role/infra/database/role.orm-entity.ts +4 -1
- package/dist/boilerplates/nest-template/src/modules/role/infra/database/role.repository.adapter.ts +2 -2
- package/dist/boilerplates/nest-template/src/modules/role/infra/http/role.controller.ts +1 -1
- package/dist/boilerplates/nest-template/src/modules/storage/infra/http/storage.controller.ts +1 -1
- package/dist/boilerplates/nest-template/src/modules/user/infra/database/user.orm-entity.ts +4 -1
- package/dist/boilerplates/nest-template/src/modules/user/infra/database/user.repository.adapter.ts +2 -2
- package/dist/boilerplates/nest-template/src/modules/user/infra/http/user.controller.ts +1 -1
- package/dist/boilerplates/nest-template/src/modules/user/service/user.service.ts +6 -3
- package/dist/boilerplates/nest-template/src/platform/security/security.module.ts +20 -0
- package/dist/boilerplates/nest-template/src/platform/security/security.tokens.ts +4 -0
- package/dist/boilerplates/nest-template/src/platform/security/token-verification.module.ts +22 -0
- package/dist/boilerplates/nest-template/tsconfig.json +1 -1
- package/dist/generation/backend-generator.integration.test.d.ts +1 -0
- package/dist/generation/backend-generator.integration.test.js +251 -0
- package/dist/generation/backend-layout.d.ts +15 -0
- package/dist/generation/backend-layout.js +77 -0
- package/dist/generation/backend-layout.test.d.ts +1 -0
- package/dist/generation/backend-layout.test.js +48 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +389 -210
- package/dist/migrations/entity-module-move.d.ts +16 -0
- package/dist/migrations/entity-module-move.js +98 -0
- package/dist/migrations/entity-module-move.test.d.ts +1 -0
- package/dist/migrations/entity-module-move.test.js +30 -0
- package/dist/rules/linter-rules.js +252 -22
- package/dist/rules/linter-rules.test.js +125 -0
- package/dist/templates/angular/service.template.d.ts +1 -1
- package/dist/templates/angular/service.template.js +2 -2
- package/dist/templates/generator-characterization.test.d.ts +1 -0
- package/dist/templates/generator-characterization.test.js +42 -0
- package/dist/templates/nest/crud.templates.d.ts +3 -3
- package/dist/templates/nest/crud.templates.js +20 -13
- package/dist/templates/nest/functional-module.templates.d.ts +10 -0
- package/dist/templates/nest/functional-module.templates.js +261 -0
- package/dist/templates/nest/index.d.ts +1 -1
- package/dist/templates/nest/index.js +1 -1
- package/dist/templates/nest/migration.template.js +6 -3
- package/dist/templates/relationship-templates.test.js +0 -4
- package/package.json +4 -4
- package/dist/boilerplates/angular-template/node_modules/.bin/acorn +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/browserslist +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/eslint +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/jiti +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/js-yaml +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/lessc +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/ng +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/ng-xi18n +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/ngc +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/nx +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/nx-cloud +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/sass +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/terser +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/tsc +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/tsserver +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/vite +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/vitest +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/webpack +0 -21
- package/dist/boilerplates/angular-template/node_modules/.bin/yaml +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/acorn +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/nest +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/prettier +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/ts-node +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/ts-node-cwd +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/ts-node-esm +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/ts-node-script +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/ts-node-transpile-only +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/ts-script +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/tsc +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/tsserver +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/typeorm +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/typeorm-ts-node-commonjs +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/typeorm-ts-node-esm +0 -21
- package/dist/boilerplates/nest-template/node_modules/.bin/webpack +0 -21
- package/dist/templates/nest/microservice.templates.d.ts +0 -4
- package/dist/templates/nest/microservice.templates.js +0 -157
package/dist/index.js
CHANGED
|
@@ -34,15 +34,21 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
};
|
|
35
35
|
})();
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.program = void 0;
|
|
38
|
+
exports.patchLocalDependencies = patchLocalDependencies;
|
|
37
39
|
const commander_1 = require("commander");
|
|
38
40
|
const path = __importStar(require("path"));
|
|
39
41
|
const fs = __importStar(require("fs-extra"));
|
|
40
42
|
const cli_templates_1 = require("./templates/cli-templates");
|
|
43
|
+
const functional_module_templates_1 = require("./templates/nest/functional-module.templates");
|
|
41
44
|
const ui_templates_1 = require("./templates/ui-templates");
|
|
42
45
|
const linter_rules_1 = require("./rules/linter-rules");
|
|
43
46
|
const relationships_1 = require("./templates/shared/relationships");
|
|
44
47
|
const sort_entities_1 = require("./migrations/sort-entities");
|
|
45
48
|
const child_process_1 = require("child_process");
|
|
49
|
+
const devkit_core_1 = require("@devstroupe/devkit-core");
|
|
50
|
+
const backend_layout_1 = require("./generation/backend-layout");
|
|
51
|
+
const entity_module_move_1 = require("./migrations/entity-module-move");
|
|
46
52
|
// Registra ts-node para poder carregar arquivos .ts (como o config)
|
|
47
53
|
try {
|
|
48
54
|
require('ts-node').register({
|
|
@@ -53,6 +59,7 @@ catch (e) {
|
|
|
53
59
|
// Silencioso se ts-node não estiver presente localmente
|
|
54
60
|
}
|
|
55
61
|
const program = new commander_1.Command();
|
|
62
|
+
exports.program = program;
|
|
56
63
|
const SPARTAN_STYLES = ['nova', 'vega', 'lyra', 'maia', 'mira', 'luma'];
|
|
57
64
|
function isSpartanStyle(value) {
|
|
58
65
|
return SPARTAN_STYLES.includes(value);
|
|
@@ -62,12 +69,7 @@ function getDesignSystemConfig(config) {
|
|
|
62
69
|
}
|
|
63
70
|
function assertValidRelationshipConfig(config) {
|
|
64
71
|
(0, relationships_1.validateRelationships)(config.entities ?? []);
|
|
65
|
-
|
|
66
|
-
return;
|
|
67
|
-
const manyToMany = (config.entities ?? []).flatMap((item) => (0, relationships_1.getRelationships)(item.properties ?? []).filter((relation) => relation.kind === 'many-to-many'));
|
|
68
|
-
if (manyToMany.length > 0) {
|
|
69
|
-
throw new Error('Many-to-many relationships require a dedicated junction CRUD when backendType is "microservices".');
|
|
70
|
-
}
|
|
72
|
+
(0, backend_layout_1.validateModuleRelationships)(config.entities ?? []);
|
|
71
73
|
}
|
|
72
74
|
function ensureMigrationInfrastructure(backendRoot) {
|
|
73
75
|
const databaseDir = path.join(backendRoot, 'src', 'database');
|
|
@@ -111,6 +113,112 @@ function ensureInitialMigrations(backendRoot, entities, propertyOverrides = new
|
|
|
111
113
|
console.log(`[Migration] Criada ${fileName}`);
|
|
112
114
|
});
|
|
113
115
|
}
|
|
116
|
+
const CRUD_USE_CASE_KINDS = ['create', 'update', 'find', 'list', 'soft-delete'];
|
|
117
|
+
function generatedEntityReady(entityRoot, entityName) {
|
|
118
|
+
return fs.existsSync(path.join(entityRoot, entityName, 'application', 'use-cases', `${entityName}-crud.application.ts`));
|
|
119
|
+
}
|
|
120
|
+
function writeCrudEntityArtifacts(entityRoot, entity, properties, tenantScoped, moduleName) {
|
|
121
|
+
const root = path.join(entityRoot, entity.name);
|
|
122
|
+
const useCases = path.join(root, 'application', 'use-cases');
|
|
123
|
+
const ports = path.join(root, 'application', 'ports');
|
|
124
|
+
const persistence = path.join(root, 'infrastructure', 'persistence', 'typeorm');
|
|
125
|
+
const api = path.join(root, 'api');
|
|
126
|
+
[useCases, ports, persistence, api].forEach((directory) => fs.ensureDirSync(directory));
|
|
127
|
+
fs.writeFileSync(path.join(persistence, `${entity.name}.orm-entity.ts`), (0, cli_templates_1.nestOrmEntityTemplate)(entity.name, properties, entity.tableName || `${entity.name}s`, tenantScoped, entity.indexes, { ...(entity.persistence ?? {}), deletion: 'soft-delete' }, entity.automaticFields, '../../../../'), 'utf-8');
|
|
128
|
+
fs.writeFileSync(path.join(api, `${entity.name}.dto.ts`), (0, cli_templates_1.nestDtoTemplate)(entity.name, properties), 'utf-8');
|
|
129
|
+
fs.writeFileSync(path.join(ports, `${entity.name}.repository.port.ts`), (0, functional_module_templates_1.nestRepositoryPortV2Template)(entity.name), 'utf-8');
|
|
130
|
+
fs.writeFileSync(path.join(persistence, `${entity.name}.repository.ts`), (0, functional_module_templates_1.nestRepositoryAdapterV2Template)(entity.name, properties, tenantScoped), 'utf-8');
|
|
131
|
+
for (const kind of CRUD_USE_CASE_KINDS) {
|
|
132
|
+
fs.writeFileSync(path.join(useCases, `${kind}-${entity.name}.use-case.ts`), (0, functional_module_templates_1.nestCrudUseCaseTemplate)(entity.name, kind), 'utf-8');
|
|
133
|
+
}
|
|
134
|
+
fs.writeFileSync(path.join(useCases, `${entity.name}-crud.application.ts`), (0, functional_module_templates_1.nestCrudApplicationTemplate)(entity.name), 'utf-8');
|
|
135
|
+
fs.writeFileSync(path.join(api, `${entity.name}.controller.ts`), (0, functional_module_templates_1.nestControllerV2Template)(entity.name, moduleName), 'utf-8');
|
|
136
|
+
}
|
|
137
|
+
function generatedMembers(moduleRoot, entities, current) {
|
|
138
|
+
return entities.filter((candidate) => candidate.name === current.name || generatedEntityReady(moduleRoot, candidate.name));
|
|
139
|
+
}
|
|
140
|
+
function migrationPropertiesForEntity(entity, config) {
|
|
141
|
+
const properties = (0, backend_layout_1.persistencePropertiesForEntity)(entity, config.entities ?? []);
|
|
142
|
+
const tenantScoped = Boolean(config.multiTenant?.enabled && entity.tenantScoped !== false);
|
|
143
|
+
return tenantScoped && !properties.some((property) => property.name === 'tenantId')
|
|
144
|
+
? [...properties, { name: 'tenantId', label: 'Tenant', type: 'string', required: true, index: true }]
|
|
145
|
+
: properties;
|
|
146
|
+
}
|
|
147
|
+
function platformPackageName(projectName) {
|
|
148
|
+
const scope = String(projectName || 'app')
|
|
149
|
+
.trim().toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '') || 'app';
|
|
150
|
+
return `@${scope}/platform`;
|
|
151
|
+
}
|
|
152
|
+
function linkPlatformPackage(applicationRoot, platformRoot, packageName) {
|
|
153
|
+
const packagePath = path.join(applicationRoot, 'package.json');
|
|
154
|
+
if (!fs.existsSync(packagePath))
|
|
155
|
+
return;
|
|
156
|
+
const localPackageRoot = path.join(applicationRoot, 'local_packages', 'project-platform');
|
|
157
|
+
fs.removeSync(localPackageRoot);
|
|
158
|
+
fs.copySync(platformRoot, localPackageRoot, {
|
|
159
|
+
filter: (source) => !source.split(path.sep).some((part) => ['node_modules', 'dist'].includes(part)),
|
|
160
|
+
});
|
|
161
|
+
const packageJson = fs.readJsonSync(packagePath);
|
|
162
|
+
packageJson.dependencies ??= {};
|
|
163
|
+
packageJson.dependencies[packageName] = 'file:local_packages/project-platform';
|
|
164
|
+
fs.writeJsonSync(packagePath, packageJson, { spaces: 2 });
|
|
165
|
+
}
|
|
166
|
+
function ensurePlatformLayout(backendRoot, microservices, projectName = 'app') {
|
|
167
|
+
const platformRoot = microservices
|
|
168
|
+
? path.join(backendRoot, 'packages', 'platform')
|
|
169
|
+
: path.join(backendRoot, 'src', 'platform');
|
|
170
|
+
const capabilitiesRoot = microservices ? path.join(platformRoot, 'src') : platformRoot;
|
|
171
|
+
const capabilities = ['security', 'tenancy', 'http', 'messaging', 'observability', 'storage', 'integrations'];
|
|
172
|
+
capabilities.forEach((capability) => {
|
|
173
|
+
const capabilityRoot = path.join(capabilitiesRoot, capability);
|
|
174
|
+
fs.ensureDirSync(capabilityRoot);
|
|
175
|
+
const capabilityReadme = path.join(capabilityRoot, 'README.md');
|
|
176
|
+
if (!fs.existsSync(capabilityReadme)) {
|
|
177
|
+
fs.writeFileSync(capabilityReadme, `# ${capability}\n\nAdapters técnicos compartilhados de ${capability}. Não importe entities ou repositories de negócio aqui.\n`, 'utf-8');
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
const readmePath = path.join(platformRoot, 'README.md');
|
|
181
|
+
if (!fs.existsSync(readmePath)) {
|
|
182
|
+
fs.writeFileSync(readmePath, `# Platform\n\nInfraestrutura técnica compartilhada. Este diretório não representa um microsserviço.\n\n- Contratos de negócio permanecem nos módulos funcionais.\n- Integrações compartilhadas são nomeadas pela capacidade.\n- Platform nunca importa entities ou repositories de negócio.\n- Em microsserviços, edite este package central; as cópias em local_packages são sincronizadas pelo gerador.\n- Promova uma capacidade a serviço somente quando estado, quota, chave única, persistência, fila, auditoria ou governança central exigirem ownership próprio.\n`, 'utf-8');
|
|
183
|
+
}
|
|
184
|
+
if (microservices) {
|
|
185
|
+
const requestedPackageName = platformPackageName(projectName);
|
|
186
|
+
const platformPackagePath = path.join(platformRoot, 'package.json');
|
|
187
|
+
if (!fs.existsSync(platformPackagePath)) {
|
|
188
|
+
fs.writeJsonSync(platformPackagePath, {
|
|
189
|
+
name: requestedPackageName,
|
|
190
|
+
version: '0.0.0',
|
|
191
|
+
private: true,
|
|
192
|
+
main: './src/index.js',
|
|
193
|
+
types: './src/index.d.ts',
|
|
194
|
+
peerDependencies: { '@devstroupe/devkit-nest': `^${cliVersion}` },
|
|
195
|
+
}, { spaces: 2 });
|
|
196
|
+
}
|
|
197
|
+
const packageName = fs.readJsonSync(platformPackagePath).name;
|
|
198
|
+
const rootJs = path.join(capabilitiesRoot, 'index.js');
|
|
199
|
+
const rootTypes = path.join(capabilitiesRoot, 'index.d.ts');
|
|
200
|
+
if (!fs.existsSync(rootJs))
|
|
201
|
+
fs.writeFileSync(rootJs, `module.exports = require('@devstroupe/devkit-nest');\n`, 'utf-8');
|
|
202
|
+
if (!fs.existsSync(rootTypes))
|
|
203
|
+
fs.writeFileSync(rootTypes, `export * from '@devstroupe/devkit-nest';\n`, 'utf-8');
|
|
204
|
+
for (const capability of capabilities) {
|
|
205
|
+
const capabilityRoot = path.join(capabilitiesRoot, capability);
|
|
206
|
+
const capabilityJs = path.join(capabilityRoot, 'index.js');
|
|
207
|
+
const capabilityTypes = path.join(capabilityRoot, 'index.d.ts');
|
|
208
|
+
if (!fs.existsSync(capabilityJs))
|
|
209
|
+
fs.writeFileSync(capabilityJs, `module.exports = require('@devstroupe/devkit-nest');\n`, 'utf-8');
|
|
210
|
+
if (!fs.existsSync(capabilityTypes))
|
|
211
|
+
fs.writeFileSync(capabilityTypes, `export * from '@devstroupe/devkit-nest';\n`, 'utf-8');
|
|
212
|
+
}
|
|
213
|
+
const applicationRoots = [
|
|
214
|
+
path.join(backendRoot, 'api-gateway'),
|
|
215
|
+
...(fs.existsSync(path.join(backendRoot, 'apps'))
|
|
216
|
+
? fs.readdirSync(path.join(backendRoot, 'apps')).map((entry) => path.join(backendRoot, 'apps', entry))
|
|
217
|
+
: []),
|
|
218
|
+
];
|
|
219
|
+
applicationRoots.forEach((applicationRoot) => linkPlatformPackage(applicationRoot, platformRoot, packageName));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
114
222
|
function getSpartanStyle(config) {
|
|
115
223
|
const designSystem = getDesignSystemConfig(config);
|
|
116
224
|
const configuredStyle = designSystem.spartanStyle || designSystem.style || designSystem.preset || 'vega';
|
|
@@ -1105,13 +1213,6 @@ program
|
|
|
1105
1213
|
const isMultiTenant = !!config.multiTenant?.enabled;
|
|
1106
1214
|
const isTenantScoped = isMultiTenant && (entity.tenantScoped !== false);
|
|
1107
1215
|
const isMicroservices = config.backendType === 'microservices';
|
|
1108
|
-
const backendProperties = isMicroservices
|
|
1109
|
-
? entity.properties
|
|
1110
|
-
.filter((property) => property.type !== 'relationship' || (0, relationships_1.getRelationships)([property])[0].kind !== 'one-to-many')
|
|
1111
|
-
.map((property) => property.type === 'relationship'
|
|
1112
|
-
? { ...property, type: 'number', name: (0, relationships_1.getRelationships)([property])[0].foreignKey }
|
|
1113
|
-
: property)
|
|
1114
|
-
: entity.properties;
|
|
1115
1216
|
let boilerplatesDir = path.resolve(__dirname, 'boilerplates');
|
|
1116
1217
|
if (!fs.existsSync(boilerplatesDir)) {
|
|
1117
1218
|
boilerplatesDir = path.resolve(__dirname, '../boilerplates');
|
|
@@ -1120,131 +1221,64 @@ program
|
|
|
1120
1221
|
boilerplatesDir = path.resolve(__dirname, '../../boilerplates');
|
|
1121
1222
|
}
|
|
1122
1223
|
const nestTemplateDir = path.join(boilerplatesDir, 'nest-template');
|
|
1224
|
+
const group = (0, backend_layout_1.moduleForEntity)(entity, config.entities ?? []);
|
|
1225
|
+
const backendProperties = (0, backend_layout_1.persistencePropertiesForEntity)(entity, config.entities ?? []);
|
|
1123
1226
|
if (isMicroservices) {
|
|
1124
|
-
const entityIndex = config.entities.findIndex((e) => e.name.toLowerCase() === entity.name.toLowerCase());
|
|
1125
|
-
const port = 3001 + (entityIndex >= 0 ? entityIndex : 0);
|
|
1126
|
-
// Garantir estrutura do API Gateway
|
|
1127
1227
|
const gatewayRoot = path.join(backendRoot, 'api-gateway');
|
|
1128
|
-
if (!fs.existsSync(
|
|
1129
|
-
console.log(
|
|
1228
|
+
if (!fs.existsSync(path.join(gatewayRoot, 'src', 'app.module.ts'))) {
|
|
1229
|
+
console.log('[Gateway] Inicializando estrutura do API Gateway...');
|
|
1130
1230
|
fs.ensureDirSync(gatewayRoot);
|
|
1131
|
-
fs.copySync(nestTemplateDir, gatewayRoot);
|
|
1132
|
-
const gatewayEnv = `PORT=13000
|
|
1133
|
-
DB_HOST=database
|
|
1134
|
-
DB_PORT=3306
|
|
1135
|
-
DB_USER=root
|
|
1136
|
-
DB_PASSWORD=root
|
|
1137
|
-
DB_NAME=devstroupe
|
|
1138
|
-
JWT_SECRET=devstroupe-secret-key-12345
|
|
1139
|
-
`;
|
|
1231
|
+
fs.copySync(nestTemplateDir, gatewayRoot, { filter: createBoilerplateCopyFilter(nestTemplateDir) });
|
|
1232
|
+
const gatewayEnv = `PORT=13000\nJWT_SECRET=devstroupe-secret-key-12345\n`;
|
|
1140
1233
|
fs.writeFileSync(path.join(gatewayRoot, '.env'), gatewayEnv, 'utf-8');
|
|
1141
1234
|
fs.writeFileSync(path.join(gatewayRoot, '.env.example'), gatewayEnv, 'utf-8');
|
|
1142
1235
|
patchLocalDependencies(gatewayRoot, null, boilerplatesDir);
|
|
1143
|
-
|
|
1144
|
-
console.log(`[Gateway] Instalando dependências em: ${gatewayRoot}...`);
|
|
1145
|
-
(0, child_process_1.execSync)('npm install', { cwd: gatewayRoot, stdio: 'inherit' });
|
|
1146
|
-
}
|
|
1147
|
-
catch (err) {
|
|
1148
|
-
console.warn(`[Aviso] Falha ao rodar "npm install" no API Gateway. Instale manualmente.`);
|
|
1149
|
-
}
|
|
1236
|
+
prepareStatelessGateway(gatewayRoot);
|
|
1150
1237
|
}
|
|
1151
|
-
const msRoot = path.join(backendRoot, '
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
console.log(`[Microsserviço] Inicializando estrutura do microsserviço...`);
|
|
1238
|
+
const msRoot = path.join(backendRoot, 'apps', group.serviceName);
|
|
1239
|
+
if (!fs.existsSync(path.join(msRoot, 'src', 'app.module.ts'))) {
|
|
1240
|
+
console.log(`[Microsserviço] Inicializando ${group.serviceName}...`);
|
|
1155
1241
|
fs.ensureDirSync(msRoot);
|
|
1156
|
-
fs.copySync(nestTemplateDir, msRoot);
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
`;
|
|
1242
|
+
fs.copySync(nestTemplateDir, msRoot, { filter: createBoilerplateCopyFilter(nestTemplateDir) });
|
|
1243
|
+
const isCoreService = group.name === 'core';
|
|
1244
|
+
if (!isCoreService) {
|
|
1245
|
+
const jwtGuard = fs.readFileSync(path.join(msRoot, 'src', 'modules', 'auth', 'jwt-auth.guard.ts'), 'utf-8');
|
|
1246
|
+
for (const moduleName of ['user', 'auth', 'role', 'storage', 'core']) {
|
|
1247
|
+
fs.removeSync(path.join(msRoot, 'src', 'modules', moduleName));
|
|
1248
|
+
}
|
|
1249
|
+
const authRoot = path.join(msRoot, 'src', 'modules', 'auth');
|
|
1250
|
+
fs.ensureDirSync(authRoot);
|
|
1251
|
+
fs.writeFileSync(path.join(authRoot, 'jwt-auth.guard.ts'), jwtGuard, 'utf-8');
|
|
1252
|
+
fs.emptyDirSync(path.join(msRoot, 'src', 'database', 'migrations'));
|
|
1253
|
+
}
|
|
1254
|
+
fs.writeFileSync(path.join(msRoot, 'src', 'main.ts'), generateMsMainTs(group.port), 'utf-8');
|
|
1255
|
+
fs.writeFileSync(path.join(msRoot, 'src', 'app.module.ts'), generateMsAppModule(group.name, isCoreService), 'utf-8');
|
|
1256
|
+
const msEnv = `PORT=${group.port}\nDB_HOST=database\nDB_PORT=3306\nDB_USER=root\nDB_PASSWORD=root\nDB_NAME=${group.databaseName}\nSERVICE_PORT=${group.port}\n`;
|
|
1172
1257
|
fs.writeFileSync(path.join(msRoot, '.env'), msEnv, 'utf-8');
|
|
1173
1258
|
fs.writeFileSync(path.join(msRoot, '.env.example'), msEnv, 'utf-8');
|
|
1174
1259
|
patchLocalDependencies(msRoot, null, boilerplatesDir);
|
|
1175
|
-
try {
|
|
1176
|
-
console.log(`[Microsserviço] Instalando dependências em: ${msRoot}...`);
|
|
1177
|
-
(0, child_process_1.execSync)('npm install', { cwd: msRoot, stdio: 'inherit' });
|
|
1178
|
-
}
|
|
1179
|
-
catch (err) {
|
|
1180
|
-
console.warn(`[Aviso] Falha ao rodar "npm install" no microsserviço. Instale manualmente.`);
|
|
1181
|
-
}
|
|
1182
1260
|
}
|
|
1183
|
-
const
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
fs.
|
|
1187
|
-
fs.ensureDirSync(path.join(modulePath, 'infra', 'http'));
|
|
1188
|
-
// Domain Entity
|
|
1189
|
-
fs.writeFileSync(path.join(modulePath, 'domain', `${entity.name}.ts`), (0, cli_templates_1.nestDomainEntityTemplate)(entity.name, backendProperties, isTenantScoped, entity.automaticFields), 'utf-8');
|
|
1190
|
-
// Repository Port
|
|
1191
|
-
fs.writeFileSync(path.join(modulePath, 'domain', `${entity.name}.repository.port.ts`), (0, cli_templates_1.nestRepositoryPortTemplate)(entity.name), 'utf-8');
|
|
1192
|
-
// DTO
|
|
1193
|
-
fs.writeFileSync(path.join(modulePath, 'infra', 'http', `${entity.name}.dto.ts`), (0, cli_templates_1.nestDtoTemplate)(entity.name, backendProperties), 'utf-8');
|
|
1194
|
-
// Controller (TCP)
|
|
1195
|
-
fs.writeFileSync(path.join(modulePath, 'infra', 'http', `${entity.name}.ms.controller.ts`), (0, cli_templates_1.nestMsControllerTemplate)(entity.name), 'utf-8');
|
|
1196
|
-
// ORM Entity
|
|
1197
|
-
fs.writeFileSync(path.join(modulePath, 'infra', 'database', `${entity.name}.orm-entity.ts`), (0, cli_templates_1.nestOrmEntityTemplate)(entity.name, backendProperties, entity.tableName || `${entity.name}s`, isTenantScoped, entity.indexes, entity.persistence, entity.automaticFields), 'utf-8');
|
|
1198
|
-
// Repository Adapter
|
|
1199
|
-
fs.writeFileSync(path.join(modulePath, 'infra', 'database', `${entity.name}.repository.ts`), (0, cli_templates_1.nestRepositoryAdapterTemplate)(entity.name, backendProperties, isTenantScoped), 'utf-8');
|
|
1200
|
-
// Service
|
|
1201
|
-
fs.writeFileSync(path.join(modulePath, 'service', `${entity.name}.service.ts`), (0, cli_templates_1.nestServiceTemplate)(entity.name), 'utf-8');
|
|
1202
|
-
// Module (TCP)
|
|
1203
|
-
fs.writeFileSync(path.join(modulePath, `${entity.name}.module.ts`), (0, cli_templates_1.nestMsModuleTemplate)(entity.name), 'utf-8');
|
|
1204
|
-
console.log(`[Microsserviço] Arquivos do microsserviço "${currentEntityName}" gerados com sucesso.`);
|
|
1261
|
+
const moduleRoot = path.join(msRoot, 'src', 'modules', group.name);
|
|
1262
|
+
writeCrudEntityArtifacts(moduleRoot, entity, backendProperties, isTenantScoped, group.name);
|
|
1263
|
+
const members = generatedMembers(moduleRoot, group.entities, entity);
|
|
1264
|
+
fs.writeFileSync(path.join(moduleRoot, `${group.name}.module.ts`), (0, functional_module_templates_1.nestFunctionalModuleTemplate)(group.name, members), 'utf-8');
|
|
1205
1265
|
if (entity.crud?.generateMigration !== false) {
|
|
1206
|
-
ensureInitialMigrations(msRoot,
|
|
1266
|
+
ensureInitialMigrations(msRoot, members, new Map(members.map((member) => [member.name, migrationPropertiesForEntity(member, config)])));
|
|
1207
1267
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
fs.
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
// Controller (Gateway)
|
|
1215
|
-
fs.writeFileSync(path.join(gatewayModulePath, 'infra', 'http', `${entity.name}.controller.ts`), (0, cli_templates_1.nestGatewayControllerTemplate)(entity.name), 'utf-8');
|
|
1216
|
-
// Module (Gateway)
|
|
1217
|
-
fs.writeFileSync(path.join(gatewayModulePath, `${entity.name}.module.ts`), (0, cli_templates_1.nestGatewayModuleTemplate)(entity.name, port), 'utf-8');
|
|
1218
|
-
// Patch app.module.ts do Gateway
|
|
1219
|
-
patchGatewayAppModule(path.join(backendRoot, 'api-gateway', 'src', 'app.module.ts'), entity.name);
|
|
1220
|
-
console.log(`[Gateway] Módulo Gateway "${currentEntityName}" gerado com sucesso.`);
|
|
1268
|
+
const gatewayModuleRoot = path.join(gatewayRoot, 'src', 'modules', group.name);
|
|
1269
|
+
fs.ensureDirSync(gatewayModuleRoot);
|
|
1270
|
+
fs.writeFileSync(path.join(gatewayModuleRoot, `${group.name}.gateway.controller.ts`), (0, functional_module_templates_1.nestGatewayModuleProxyControllerTemplate)(group.name, group.port), 'utf-8');
|
|
1271
|
+
fs.writeFileSync(path.join(gatewayModuleRoot, `${group.name}.module.ts`), (0, functional_module_templates_1.nestGatewayFunctionalModuleTemplate)(group.name, group.port), 'utf-8');
|
|
1272
|
+
patchGatewayAppModule(path.join(gatewayRoot, 'src', 'app.module.ts'), group.name);
|
|
1273
|
+
console.log(`[Microsserviço] ${currentEntityName} adicionado a ${group.serviceName}.`);
|
|
1221
1274
|
}
|
|
1222
1275
|
else {
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
const
|
|
1226
|
-
fs.
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
fs.ensureDirSync(path.join(modulePath, 'infra', 'http'));
|
|
1230
|
-
// Domain Entity
|
|
1231
|
-
fs.writeFileSync(path.join(modulePath, 'domain', `${entity.name}.ts`), (0, cli_templates_1.nestDomainEntityTemplate)(entity.name, entity.properties, isTenantScoped, entity.automaticFields), 'utf-8');
|
|
1232
|
-
// Repository Port
|
|
1233
|
-
fs.writeFileSync(path.join(modulePath, 'domain', `${entity.name}.repository.port.ts`), (0, cli_templates_1.nestRepositoryPortTemplate)(entity.name), 'utf-8');
|
|
1234
|
-
// DTO
|
|
1235
|
-
fs.writeFileSync(path.join(modulePath, 'infra', 'http', `${entity.name}.dto.ts`), (0, cli_templates_1.nestDtoTemplate)(entity.name, entity.properties), 'utf-8');
|
|
1236
|
-
// Controller
|
|
1237
|
-
fs.writeFileSync(path.join(modulePath, 'infra', 'http', `${entity.name}.controller.ts`), (0, cli_templates_1.nestControllerTemplate)(entity.name, entity.persistence), 'utf-8');
|
|
1238
|
-
// ORM Entity
|
|
1239
|
-
fs.writeFileSync(path.join(modulePath, 'infra', 'database', `${entity.name}.orm-entity.ts`), (0, cli_templates_1.nestOrmEntityTemplate)(entity.name, entity.properties, entity.tableName || `${entity.name}s`, isTenantScoped, entity.indexes, entity.persistence, entity.automaticFields), 'utf-8');
|
|
1240
|
-
// Repository Adapter
|
|
1241
|
-
fs.writeFileSync(path.join(modulePath, 'infra', 'database', `${entity.name}.repository.ts`), (0, cli_templates_1.nestRepositoryAdapterTemplate)(entity.name, entity.properties, isTenantScoped), 'utf-8');
|
|
1242
|
-
// Service
|
|
1243
|
-
fs.writeFileSync(path.join(modulePath, 'service', `${entity.name}.service.ts`), (0, cli_templates_1.nestServiceTemplate)(entity.name), 'utf-8');
|
|
1244
|
-
// Module
|
|
1245
|
-
fs.writeFileSync(path.join(modulePath, `${entity.name}.module.ts`), (0, cli_templates_1.nestModuleTemplate)(entity.name), 'utf-8');
|
|
1246
|
-
console.log(`[NestJS] Módulo Monolito "${currentEntityName}" gerado com sucesso em: ${modulePath}`);
|
|
1247
|
-
patchMonolithAppModule(path.join(backendRoot, 'src', 'app.module.ts'), entity.name);
|
|
1276
|
+
const moduleRoot = path.join(backendRoot, 'src', 'modules', group.name);
|
|
1277
|
+
writeCrudEntityArtifacts(moduleRoot, entity, backendProperties, isTenantScoped, group.name);
|
|
1278
|
+
const members = generatedMembers(moduleRoot, group.entities, entity);
|
|
1279
|
+
fs.writeFileSync(path.join(moduleRoot, `${group.name}.module.ts`), (0, functional_module_templates_1.nestFunctionalModuleTemplate)(group.name, members), 'utf-8');
|
|
1280
|
+
patchMonolithAppModule(path.join(backendRoot, 'src', 'app.module.ts'), group.name);
|
|
1281
|
+
console.log(`[NestJS] ${currentEntityName} adicionado ao módulo funcional ${group.name}.`);
|
|
1248
1282
|
}
|
|
1249
1283
|
}
|
|
1250
1284
|
// 2. Gerar Frontend (Angular)
|
|
@@ -1268,7 +1302,7 @@ SERVICE_PORT=${port}
|
|
|
1268
1302
|
fs.ensureDirSync(path.join(modulePath, 'form-dialog-handler'));
|
|
1269
1303
|
}
|
|
1270
1304
|
// Angular Service
|
|
1271
|
-
fs.writeFileSync(path.join(servicePath, `${entity.name}.service.ts`), (0, cli_templates_1.angularServiceTemplate)(entity.name), 'utf-8');
|
|
1305
|
+
fs.writeFileSync(path.join(servicePath, `${entity.name}.service.ts`), (0, cli_templates_1.angularServiceTemplate)(entity.name, (0, devkit_core_1.resolveEntityModule)(entity)), 'utf-8');
|
|
1272
1306
|
// List HTML
|
|
1273
1307
|
fs.writeFileSync(path.join(modulePath, 'list', `${entity.name}-list.component.html`), (0, cli_templates_1.angularListHtmlTemplate)(entity.name, entity.properties, listType, formType, filterType), 'utf-8');
|
|
1274
1308
|
// List TS
|
|
@@ -1287,11 +1321,15 @@ SERVICE_PORT=${port}
|
|
|
1287
1321
|
// 3. Pós-processamento geral (depois de gerar todas as entidades selecionadas)
|
|
1288
1322
|
// Sincronizar migrações no Monolito, se houver
|
|
1289
1323
|
const isMicroservices = config.backendType === 'microservices';
|
|
1324
|
+
ensurePlatformLayout(backendRoot, isMicroservices, config.projectName);
|
|
1290
1325
|
if (!isMicroservices) {
|
|
1291
1326
|
const migrationEntities = (config.entities ?? []).filter((configuredEntity) => configuredEntity.crud?.generateBackend !== false
|
|
1292
1327
|
&& configuredEntity.crud?.generateMigration !== false);
|
|
1293
1328
|
if (migrationEntities.length > 0) {
|
|
1294
|
-
ensureInitialMigrations(backendRoot, migrationEntities)
|
|
1329
|
+
ensureInitialMigrations(backendRoot, migrationEntities, new Map(migrationEntities.map((member) => [
|
|
1330
|
+
member.name,
|
|
1331
|
+
migrationPropertiesForEntity(member, config),
|
|
1332
|
+
])));
|
|
1295
1333
|
}
|
|
1296
1334
|
}
|
|
1297
1335
|
// Regerar docker-compose.yml e init.sql
|
|
@@ -1309,6 +1347,23 @@ SERVICE_PORT=${port}
|
|
|
1309
1347
|
console.log(`\n\x1b[32m[SUCESSO] Scaffolding de CRUD completo para "${entitiesToGenerate[0].name}" finalizado!\x1b[0m`);
|
|
1310
1348
|
}
|
|
1311
1349
|
});
|
|
1350
|
+
function hasAngularPartialCompilation(packageRoot) {
|
|
1351
|
+
const realtimeOutput = path.join(packageRoot, 'dist', 'services', 'realtime.service.js');
|
|
1352
|
+
if (!fs.existsSync(realtimeOutput))
|
|
1353
|
+
return false;
|
|
1354
|
+
const content = fs.readFileSync(realtimeOutput, 'utf-8');
|
|
1355
|
+
return content.includes('ɵɵngDeclareInjectable') || content.includes('ɵɵdefineInjectable');
|
|
1356
|
+
}
|
|
1357
|
+
function ensureLocalAngularPackageBuilt(packageRoot) {
|
|
1358
|
+
if (hasAngularPartialCompilation(packageRoot))
|
|
1359
|
+
return;
|
|
1360
|
+
console.log('[DevConfig] Build Angular local ausente ou inválido; recompilando com ngc...');
|
|
1361
|
+
(0, child_process_1.execSync)('npm run build', { cwd: packageRoot, stdio: 'inherit' });
|
|
1362
|
+
if (!hasAngularPartialCompilation(packageRoot)) {
|
|
1363
|
+
throw new Error('O build local de @devstroupe/devkit-angular não contém metadados AOT/partial do Angular. '
|
|
1364
|
+
+ 'Execute o build com ngc antes de gerar o projeto.');
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1312
1367
|
// Helper para linkar dependências locais se estiver rodando no monorepo de desenvolvimento
|
|
1313
1368
|
function patchLocalDependencies(backendDest, frontendDest, boilerplatesDir) {
|
|
1314
1369
|
const packagesDir = path.resolve(boilerplatesDir, '../../..');
|
|
@@ -1365,20 +1420,21 @@ function patchLocalDependencies(backendDest, frontendDest, boilerplatesDir) {
|
|
|
1365
1420
|
const coreSrc = path.join(packagesDir, 'devkit-core');
|
|
1366
1421
|
const nestSrc = path.join(packagesDir, 'devkit-nest');
|
|
1367
1422
|
const angularSrc = path.join(packagesDir, 'devkit-angular');
|
|
1423
|
+
if (frontendDest && fs.existsSync(frontendDest) && fs.existsSync(angularSrc)) {
|
|
1424
|
+
ensureLocalAngularPackageBuilt(angularSrc);
|
|
1425
|
+
}
|
|
1368
1426
|
const stripDevelopmentDependencies = (packageDir) => {
|
|
1369
1427
|
const packagePath = path.join(packageDir, 'package.json');
|
|
1370
1428
|
if (!fs.existsSync(packagePath))
|
|
1371
1429
|
return;
|
|
1372
1430
|
const packageJson = fs.readJsonSync(packagePath);
|
|
1373
1431
|
delete packageJson.devDependencies;
|
|
1374
|
-
delete packageJson.peerDependencies;
|
|
1375
|
-
delete packageJson.dependencies;
|
|
1376
1432
|
packageJson.scripts = {};
|
|
1377
1433
|
fs.writeJsonSync(packagePath, packageJson, { spaces: 2 });
|
|
1378
1434
|
};
|
|
1379
1435
|
const copyFilter = (src) => {
|
|
1380
1436
|
const relativePath = path.relative(packagesDir, src);
|
|
1381
|
-
const ignoreList = ['node_modules', '.git', '.angular', 'tsconfig.json', 'tsconfig.build.json', 'src', 'tests', 'test'];
|
|
1437
|
+
const ignoreList = ['node_modules', '.git', '.angular', 'dist-test', 'tsconfig.json', 'tsconfig.build.json', 'src', 'tests', 'test'];
|
|
1382
1438
|
const parts = relativePath.split(path.sep);
|
|
1383
1439
|
if (parts.length > 1) {
|
|
1384
1440
|
const subParts = parts.slice(1);
|
|
@@ -1397,13 +1453,13 @@ function patchLocalDependencies(backendDest, frontendDest, boilerplatesDir) {
|
|
|
1397
1453
|
const backendLocalPackages = path.join(backendDest, 'local_packages');
|
|
1398
1454
|
if (fs.existsSync(coreSrc)) {
|
|
1399
1455
|
const coreDest = path.join(backendLocalPackages, 'devkit-core');
|
|
1400
|
-
fs.
|
|
1456
|
+
fs.emptyDirSync(coreDest);
|
|
1401
1457
|
fs.copySync(coreSrc, coreDest, { filter: copyFilter });
|
|
1402
1458
|
stripDevelopmentDependencies(coreDest);
|
|
1403
1459
|
}
|
|
1404
1460
|
if (fs.existsSync(nestSrc)) {
|
|
1405
1461
|
const nestDest = path.join(backendLocalPackages, 'devkit-nest');
|
|
1406
|
-
fs.
|
|
1462
|
+
fs.emptyDirSync(nestDest);
|
|
1407
1463
|
fs.copySync(nestSrc, nestDest, { filter: copyFilter });
|
|
1408
1464
|
stripDevelopmentDependencies(nestDest);
|
|
1409
1465
|
// Correção de workspace:* para file:../devkit-core
|
|
@@ -1442,17 +1498,19 @@ function patchLocalDependencies(backendDest, frontendDest, boilerplatesDir) {
|
|
|
1442
1498
|
console.error('[Erro] Falha ao injetar links locais no backend:', err);
|
|
1443
1499
|
}
|
|
1444
1500
|
}
|
|
1445
|
-
//
|
|
1501
|
+
// File dependencies keep their runtime dependencies under local_packages.
|
|
1502
|
+
// Removing that node_modules directory makes packaged drivers unavailable.
|
|
1503
|
+
// Peer dependencies remain resolved by the application root.
|
|
1446
1504
|
const backendDockerfile = path.join(backendDest, 'Dockerfile');
|
|
1447
1505
|
if (fs.existsSync(backendDockerfile)) {
|
|
1448
1506
|
try {
|
|
1449
1507
|
let content = fs.readFileSync(backendDockerfile, 'utf-8');
|
|
1450
1508
|
const targetStr = 'COPY package*.json ./';
|
|
1451
1509
|
const replacementStr = 'COPY package*.json ./\nCOPY local_packages/ ./local_packages/';
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
content = content.
|
|
1510
|
+
if (!content.includes('COPY local_packages/ ./local_packages/')) {
|
|
1511
|
+
content = content.split(targetStr).join(replacementStr);
|
|
1512
|
+
}
|
|
1513
|
+
content = content.replaceAll(' && rm -rf local_packages/*/node_modules', '');
|
|
1456
1514
|
fs.writeFileSync(backendDockerfile, content, 'utf-8');
|
|
1457
1515
|
console.log('[DevConfig] Dockerfile do backend ajustado para incluir dependências locais.');
|
|
1458
1516
|
}
|
|
@@ -1485,13 +1543,13 @@ function patchLocalDependencies(backendDest, frontendDest, boilerplatesDir) {
|
|
|
1485
1543
|
const frontendLocalPackages = path.join(frontendDest, 'local_packages');
|
|
1486
1544
|
if (fs.existsSync(coreSrc)) {
|
|
1487
1545
|
const coreDest = path.join(frontendLocalPackages, 'devkit-core');
|
|
1488
|
-
fs.
|
|
1546
|
+
fs.emptyDirSync(coreDest);
|
|
1489
1547
|
fs.copySync(coreSrc, coreDest, { filter: copyFilter });
|
|
1490
1548
|
stripDevelopmentDependencies(coreDest);
|
|
1491
1549
|
}
|
|
1492
1550
|
if (fs.existsSync(angularSrc)) {
|
|
1493
1551
|
const angularDest = path.join(frontendLocalPackages, 'devkit-angular');
|
|
1494
|
-
fs.
|
|
1552
|
+
fs.emptyDirSync(angularDest);
|
|
1495
1553
|
fs.copySync(angularSrc, angularDest, { filter: copyFilter });
|
|
1496
1554
|
stripDevelopmentDependencies(angularDest);
|
|
1497
1555
|
// Correção de workspace:* para file:../devkit-core
|
|
@@ -1530,16 +1588,17 @@ function patchLocalDependencies(backendDest, frontendDest, boilerplatesDir) {
|
|
|
1530
1588
|
console.error('[Erro] Falha ao injetar links locais no frontend:', err);
|
|
1531
1589
|
}
|
|
1532
1590
|
}
|
|
1533
|
-
//
|
|
1591
|
+
// Preserve runtime dependencies installed for local file packages.
|
|
1534
1592
|
const frontendDockerfile = path.join(frontendDest, 'Dockerfile');
|
|
1535
1593
|
if (fs.existsSync(frontendDockerfile)) {
|
|
1536
1594
|
try {
|
|
1537
1595
|
let content = fs.readFileSync(frontendDockerfile, 'utf-8');
|
|
1538
1596
|
const targetStr = 'COPY package*.json ./';
|
|
1539
1597
|
const replacementStr = 'COPY package*.json ./\nCOPY local_packages/ ./local_packages/';
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1598
|
+
if (!content.includes('COPY local_packages/ ./local_packages/')) {
|
|
1599
|
+
content = content.split(targetStr).join(replacementStr);
|
|
1600
|
+
}
|
|
1601
|
+
content = content.replaceAll(' && rm -rf local_packages/*/node_modules', '');
|
|
1543
1602
|
fs.writeFileSync(frontendDockerfile, content, 'utf-8');
|
|
1544
1603
|
console.log('[DevConfig] Dockerfile do frontend ajustado para incluir dependências locais.');
|
|
1545
1604
|
}
|
|
@@ -1607,32 +1666,51 @@ if (fs.existsSync(envPath)) {
|
|
|
1607
1666
|
}
|
|
1608
1667
|
|
|
1609
1668
|
import { NestFactory } from '@nestjs/core';
|
|
1610
|
-
import { Transport, MicroserviceOptions } from '@nestjs/microservices';
|
|
1611
1669
|
import { AppModule } from './app.module';
|
|
1670
|
+
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
|
1612
1671
|
|
|
1613
1672
|
async function bootstrap() {
|
|
1614
1673
|
const port = Number(process.env.SERVICE_PORT) || ${port};
|
|
1615
|
-
const app = await NestFactory.
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
});
|
|
1622
|
-
await app.listen();
|
|
1623
|
-
console.log(\`Microsserviço iniciado via TCP na porta \${port}\`);
|
|
1674
|
+
const app = await NestFactory.create(AppModule);
|
|
1675
|
+
app.setGlobalPrefix('api');
|
|
1676
|
+
const swagger = new DocumentBuilder().setTitle('Module API').setVersion('1.0').addBearerAuth().build();
|
|
1677
|
+
SwaggerModule.setup('docs', app, SwaggerModule.createDocument(app, swagger));
|
|
1678
|
+
await app.listen(port, '0.0.0.0');
|
|
1679
|
+
console.log(\`Serviço HTTP iniciado na porta \${port}\`);
|
|
1624
1680
|
}
|
|
1625
1681
|
bootstrap();
|
|
1626
1682
|
`;
|
|
1627
1683
|
}
|
|
1628
|
-
function generateMsAppModule(entityName) {
|
|
1684
|
+
function generateMsAppModule(entityName, coreService) {
|
|
1629
1685
|
const pascalName = kebabToPascal(entityName);
|
|
1686
|
+
const securityImport = coreService
|
|
1687
|
+
? `import { SecurityModule } from './platform/security/security.module';`
|
|
1688
|
+
: `import { TokenVerificationModule } from './platform/security/token-verification.module';`;
|
|
1689
|
+
const securityModule = coreService ? 'SecurityModule' : 'TokenVerificationModule';
|
|
1690
|
+
const storageImports = coreService
|
|
1691
|
+
? `import { StorageModule } from '@devstroupe/devkit-nest';`
|
|
1692
|
+
: '';
|
|
1693
|
+
const storageRegistration = coreService
|
|
1694
|
+
? `
|
|
1695
|
+
(StorageModule as any).registerAsync({
|
|
1696
|
+
useFactory: () => ({
|
|
1697
|
+
driver: (process.env.STORAGE_DRIVER || 'local') as 'local' | 's3' | 'r2',
|
|
1698
|
+
local: { storagePath: process.env.STORAGE_LOCAL_PATH || './storage' },
|
|
1699
|
+
}),
|
|
1700
|
+
}),`
|
|
1701
|
+
: '';
|
|
1630
1702
|
return `import { Module } from '@nestjs/common';
|
|
1703
|
+
import { APP_INTERCEPTOR } from '@nestjs/core';
|
|
1631
1704
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
1705
|
+
import { ExecutionContextInterceptor } from '@devstroupe/devkit-nest';
|
|
1706
|
+
import * as path from 'path';
|
|
1632
1707
|
import { ${pascalName}Module } from './modules/${entityName}/${entityName}.module';
|
|
1708
|
+
${securityImport}
|
|
1709
|
+
${storageImports}
|
|
1633
1710
|
|
|
1634
1711
|
@Module({
|
|
1635
1712
|
imports: [
|
|
1713
|
+
${securityModule},
|
|
1636
1714
|
TypeOrmModule.forRoot({
|
|
1637
1715
|
type: 'mysql',
|
|
1638
1716
|
host: process.env.DB_HOST || 'localhost',
|
|
@@ -1642,15 +1720,49 @@ import { ${pascalName}Module } from './modules/${entityName}/${entityName}.modul
|
|
|
1642
1720
|
database: process.env.DB_NAME || 'devstroupe_${entityName}',
|
|
1643
1721
|
autoLoadEntities: true,
|
|
1644
1722
|
synchronize: false,
|
|
1723
|
+
migrations: [path.join(__dirname, './database/migrations/*{.ts,.js}')],
|
|
1724
|
+
migrationsRun: true,
|
|
1645
1725
|
}),
|
|
1646
|
-
${pascalName}Module
|
|
1726
|
+
${pascalName}Module,${storageRegistration}
|
|
1647
1727
|
],
|
|
1648
1728
|
controllers: [],
|
|
1649
|
-
providers: [],
|
|
1729
|
+
providers: [{ provide: APP_INTERCEPTOR, useClass: ExecutionContextInterceptor }],
|
|
1650
1730
|
})
|
|
1651
1731
|
export class AppModule {}
|
|
1652
1732
|
`;
|
|
1653
1733
|
}
|
|
1734
|
+
function generateGatewayMainTs() {
|
|
1735
|
+
return `import { NestFactory } from '@nestjs/core';
|
|
1736
|
+
import { AppModule } from './app.module';
|
|
1737
|
+
|
|
1738
|
+
async function bootstrap() {
|
|
1739
|
+
const app = await NestFactory.create(AppModule, { bodyParser: false });
|
|
1740
|
+
app.setGlobalPrefix('api');
|
|
1741
|
+
app.enableCors();
|
|
1742
|
+
const port = Number(process.env.PORT) || 13000;
|
|
1743
|
+
await app.listen(port, '0.0.0.0');
|
|
1744
|
+
console.log(\`API Gateway stateless iniciado na porta \${port}\`);
|
|
1745
|
+
}
|
|
1746
|
+
bootstrap();
|
|
1747
|
+
`;
|
|
1748
|
+
}
|
|
1749
|
+
function generateGatewayAppModule() {
|
|
1750
|
+
return `import { Module } from '@nestjs/common';
|
|
1751
|
+
import { TokenVerificationModule } from './platform/security/token-verification.module';
|
|
1752
|
+
|
|
1753
|
+
@Module({
|
|
1754
|
+
imports: [TokenVerificationModule],
|
|
1755
|
+
})
|
|
1756
|
+
export class AppModule {}
|
|
1757
|
+
`;
|
|
1758
|
+
}
|
|
1759
|
+
function prepareStatelessGateway(gatewayRoot) {
|
|
1760
|
+
fs.removeSync(path.join(gatewayRoot, 'src', 'database'));
|
|
1761
|
+
fs.emptyDirSync(path.join(gatewayRoot, 'src', 'modules'));
|
|
1762
|
+
fs.removeSync(path.join(gatewayRoot, 'src', 'platform', 'security', 'security.module.ts'));
|
|
1763
|
+
fs.writeFileSync(path.join(gatewayRoot, 'src', 'main.ts'), generateGatewayMainTs(), 'utf-8');
|
|
1764
|
+
fs.writeFileSync(path.join(gatewayRoot, 'src', 'app.module.ts'), generateGatewayAppModule(), 'utf-8');
|
|
1765
|
+
}
|
|
1654
1766
|
function patchGatewayAppModule(appModulePath, entityName) {
|
|
1655
1767
|
const pascalName = kebabToPascal(entityName);
|
|
1656
1768
|
const moduleName = `${pascalName}GatewayModule`;
|
|
@@ -1667,16 +1779,11 @@ function patchGatewayAppModule(appModulePath, entityName) {
|
|
|
1667
1779
|
// Adiciona a declaração de import no topo
|
|
1668
1780
|
const importStatement = `import { ${moduleName} } from '${importPath}';\n`;
|
|
1669
1781
|
content = importStatement + content;
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
const currentImports = match[1];
|
|
1676
|
-
// Adiciona o novo módulo no início dos imports
|
|
1677
|
-
const updatedImports = `\n ${moduleName},${currentImports}`;
|
|
1678
|
-
content = content.replace(currentImports, updatedImports);
|
|
1679
|
-
}
|
|
1782
|
+
const moduleImportsRegex = /(@Module\s*\(\s*\{\s*imports\s*:\s*\[)([\s\S]*?)(\]\s*,?)/;
|
|
1783
|
+
content = content.replace(moduleImportsRegex, (_match, prefix, body, suffix) => {
|
|
1784
|
+
const imports = body.split(',').map((entry) => entry.trim()).filter(Boolean);
|
|
1785
|
+
return `${prefix}\n${[moduleName, ...imports].map((entry) => ` ${entry},`).join('\n')}\n ${suffix}`;
|
|
1786
|
+
});
|
|
1680
1787
|
fs.writeFileSync(appModulePath, content, 'utf-8');
|
|
1681
1788
|
console.log(`[Gateway] app.module.ts patcheado com sucesso para importar ${moduleName}.`);
|
|
1682
1789
|
}
|
|
@@ -1705,9 +1812,20 @@ function patchMonolithAppModule(appModulePath, entityName) {
|
|
|
1705
1812
|
fs.writeFileSync(appModulePath, content, 'utf-8');
|
|
1706
1813
|
console.log(`[NestJS] app.module.ts atualizado para importar ${moduleName}.`);
|
|
1707
1814
|
}
|
|
1815
|
+
function removeMonolithModuleImport(appModulePath, moduleName) {
|
|
1816
|
+
if (!fs.existsSync(appModulePath))
|
|
1817
|
+
return;
|
|
1818
|
+
const pascalName = kebabToPascal(moduleName);
|
|
1819
|
+
const className = `${pascalName}Module`;
|
|
1820
|
+
let content = fs.readFileSync(appModulePath, 'utf-8');
|
|
1821
|
+
content = content.replace(new RegExp(`^import \\{ ${className} \\} from ['"]\\./modules/${moduleName}/${moduleName}\\.module['"];?\\n`, 'm'), '');
|
|
1822
|
+
content = content.replace(new RegExp(`\\n\\s*${className},?`, 'g'), '');
|
|
1823
|
+
fs.writeFileSync(appModulePath, content, 'utf-8');
|
|
1824
|
+
}
|
|
1708
1825
|
function generateDockerComposeAndDbInit(projectRoot, config) {
|
|
1709
1826
|
const isMicroservices = config.backendType === 'microservices';
|
|
1710
1827
|
const projectName = config.projectName || 'devstroupe-project';
|
|
1828
|
+
const moduleGroups = (0, backend_layout_1.buildBackendModuleGroups)(config.entities ?? []);
|
|
1711
1829
|
// 1. db-init/init.sql
|
|
1712
1830
|
const dbInitDir = path.join(projectRoot, 'db-init');
|
|
1713
1831
|
fs.ensureDirSync(dbInitDir);
|
|
@@ -1715,12 +1833,9 @@ function generateDockerComposeAndDbInit(projectRoot, config) {
|
|
|
1715
1833
|
fs.chmodSync(dbInitDir, 0o755);
|
|
1716
1834
|
}
|
|
1717
1835
|
catch { }
|
|
1718
|
-
const dbs =
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
dbs.push(`devstroupe_${entity.name.toLowerCase()}`);
|
|
1722
|
-
});
|
|
1723
|
-
}
|
|
1836
|
+
const dbs = isMicroservices
|
|
1837
|
+
? moduleGroups.map((group) => group.databaseName)
|
|
1838
|
+
: ['devstroupe'];
|
|
1724
1839
|
const initSqlContent = dbs.map(db => `CREATE DATABASE IF NOT EXISTS \`${db}\`;`).join('\n') + '\n';
|
|
1725
1840
|
const sqlPath = path.join(dbInitDir, 'init.sql');
|
|
1726
1841
|
fs.writeFileSync(sqlPath, initSqlContent, 'utf-8');
|
|
@@ -1749,24 +1864,18 @@ function generateDockerComposeAndDbInit(projectRoot, config) {
|
|
|
1749
1864
|
devServicesYaml += dbServiceYaml;
|
|
1750
1865
|
if (isMicroservices) {
|
|
1751
1866
|
// Gateway service
|
|
1752
|
-
let gatewayEnv = `
|
|
1753
|
-
DB_PORT: 3306
|
|
1754
|
-
DB_USER: root
|
|
1755
|
-
DB_PASSWORD: root
|
|
1756
|
-
DB_NAME: devstroupe
|
|
1867
|
+
let gatewayEnv = ` PORT: 13000
|
|
1757
1868
|
JWT_SECRET: devstroupe-secret-key-12345
|
|
1758
1869
|
`;
|
|
1759
|
-
let gatewayDependsOn =
|
|
1760
|
-
`;
|
|
1870
|
+
let gatewayDependsOn = '';
|
|
1761
1871
|
// Adiciona variáveis de microsserviços no gateway
|
|
1762
|
-
if (
|
|
1763
|
-
|
|
1764
|
-
const
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
${nameUpper}_SERVICE_PORT: ${port}
|
|
1872
|
+
if (moduleGroups.length) {
|
|
1873
|
+
moduleGroups.forEach((group) => {
|
|
1874
|
+
const nameUpper = group.name.replace(/-/g, '_').toUpperCase();
|
|
1875
|
+
gatewayEnv += ` ${nameUpper}_SERVICE_HOST: ${group.serviceName}
|
|
1876
|
+
${nameUpper}_SERVICE_PORT: ${group.port}
|
|
1768
1877
|
`;
|
|
1769
|
-
gatewayDependsOn += ` - ${
|
|
1878
|
+
gatewayDependsOn += ` - ${group.serviceName}
|
|
1770
1879
|
`;
|
|
1771
1880
|
});
|
|
1772
1881
|
}
|
|
@@ -1796,44 +1905,45 @@ ${gatewayEnv} command: sh -c "npm install && npm run start:dev"
|
|
|
1796
1905
|
depends_on:
|
|
1797
1906
|
${gatewayDependsOn}`;
|
|
1798
1907
|
// Containers de microsserviços
|
|
1799
|
-
if (
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
servicesYaml += `\n\n ${entity.name}-service:
|
|
1908
|
+
if (moduleGroups.length) {
|
|
1909
|
+
moduleGroups.forEach((group) => {
|
|
1910
|
+
servicesYaml += `\n\n ${group.serviceName}:
|
|
1803
1911
|
build:
|
|
1804
|
-
context: ./backend/
|
|
1912
|
+
context: ./backend/apps/${group.serviceName}
|
|
1805
1913
|
dockerfile: Dockerfile
|
|
1806
|
-
container_name: ${projectName}-${
|
|
1914
|
+
container_name: ${projectName}-${group.serviceName}
|
|
1807
1915
|
restart: always
|
|
1808
|
-
|
|
1809
|
-
- "${port}
|
|
1916
|
+
expose:
|
|
1917
|
+
- "${group.port}"
|
|
1810
1918
|
environment:
|
|
1811
1919
|
DB_HOST: database
|
|
1812
1920
|
DB_PORT: 3306
|
|
1813
1921
|
DB_USER: root
|
|
1814
1922
|
DB_PASSWORD: root
|
|
1815
|
-
DB_NAME:
|
|
1816
|
-
SERVICE_PORT: ${port}
|
|
1923
|
+
DB_NAME: ${group.databaseName}
|
|
1924
|
+
SERVICE_PORT: ${group.port}
|
|
1925
|
+
JWT_SECRET: devstroupe-secret-key-12345
|
|
1817
1926
|
depends_on:
|
|
1818
1927
|
- database
|
|
1819
1928
|
`;
|
|
1820
|
-
devServicesYaml += `\n\n ${
|
|
1929
|
+
devServicesYaml += `\n\n ${group.serviceName}:
|
|
1821
1930
|
image: node:20-alpine
|
|
1822
|
-
container_name: ${projectName}-${
|
|
1931
|
+
container_name: ${projectName}-${group.serviceName}-dev
|
|
1823
1932
|
working_dir: /app
|
|
1824
1933
|
restart: always
|
|
1825
1934
|
volumes:
|
|
1826
|
-
- ./backend/
|
|
1935
|
+
- ./backend/apps/${group.serviceName}:/app
|
|
1827
1936
|
- /app/node_modules
|
|
1828
|
-
|
|
1829
|
-
- "${port}
|
|
1937
|
+
expose:
|
|
1938
|
+
- "${group.port}"
|
|
1830
1939
|
environment:
|
|
1831
1940
|
DB_HOST: database
|
|
1832
1941
|
DB_PORT: 3306
|
|
1833
1942
|
DB_USER: root
|
|
1834
1943
|
DB_PASSWORD: root
|
|
1835
|
-
DB_NAME:
|
|
1836
|
-
SERVICE_PORT: ${port}
|
|
1944
|
+
DB_NAME: ${group.databaseName}
|
|
1945
|
+
SERVICE_PORT: ${group.port}
|
|
1946
|
+
JWT_SECRET: devstroupe-secret-key-12345
|
|
1837
1947
|
command: sh -c "npm install && npm run start:dev"
|
|
1838
1948
|
depends_on:
|
|
1839
1949
|
- database
|
|
@@ -1864,7 +1974,7 @@ ${gatewayDependsOn}`;
|
|
|
1864
1974
|
- "14200:4200"
|
|
1865
1975
|
command: sh -c "npm install && npm run start -- --host 0.0.0.0 --poll 2000"
|
|
1866
1976
|
depends_on:
|
|
1867
|
-
- api-gateway
|
|
1977
|
+
- api-gateway
|
|
1868
1978
|
`;
|
|
1869
1979
|
}
|
|
1870
1980
|
else {
|
|
@@ -1934,16 +2044,12 @@ ${gatewayDependsOn}`;
|
|
|
1934
2044
|
- backend
|
|
1935
2045
|
`;
|
|
1936
2046
|
}
|
|
1937
|
-
const dockerComposeContent = `
|
|
1938
|
-
|
|
1939
|
-
services:
|
|
2047
|
+
const dockerComposeContent = `services:
|
|
1940
2048
|
${servicesYaml}
|
|
1941
2049
|
volumes:
|
|
1942
2050
|
mysql_data:
|
|
1943
2051
|
`;
|
|
1944
|
-
const dockerComposeDevContent = `
|
|
1945
|
-
|
|
1946
|
-
services:
|
|
2052
|
+
const dockerComposeDevContent = `services:
|
|
1947
2053
|
${devServicesYaml}
|
|
1948
2054
|
volumes:
|
|
1949
2055
|
mysql_data:
|
|
@@ -1963,7 +2069,8 @@ function generateProject(projectRoot, config) {
|
|
|
1963
2069
|
let backendDest;
|
|
1964
2070
|
if (isMicroservices) {
|
|
1965
2071
|
backendDest = path.resolve(projectRoot, backendPathRel, 'api-gateway');
|
|
1966
|
-
fs.ensureDirSync(path.resolve(projectRoot, backendPathRel, '
|
|
2072
|
+
fs.ensureDirSync(path.resolve(projectRoot, backendPathRel, 'apps'));
|
|
2073
|
+
fs.ensureDirSync(path.resolve(projectRoot, backendPathRel, 'packages', 'platform'));
|
|
1967
2074
|
}
|
|
1968
2075
|
else {
|
|
1969
2076
|
backendDest = path.resolve(projectRoot, backendPathRel);
|
|
@@ -1991,7 +2098,9 @@ function generateProject(projectRoot, config) {
|
|
|
1991
2098
|
fs.copySync(nestTemplateDir, backendDest, { filter: createBoilerplateCopyFilter(nestTemplateDir) });
|
|
1992
2099
|
// Gerar arquivos .env e .env.example
|
|
1993
2100
|
console.log('Gerando arquivos .env e .env.example no backend...');
|
|
1994
|
-
const envContent = `PORT=13000
|
|
2101
|
+
const envContent = isMicroservices ? `PORT=13000
|
|
2102
|
+
JWT_SECRET=devstroupe-secret-key-12345
|
|
2103
|
+
` : `PORT=13000
|
|
1995
2104
|
DB_HOST=localhost
|
|
1996
2105
|
DB_PORT=3306
|
|
1997
2106
|
DB_USER=root
|
|
@@ -2016,6 +2125,8 @@ R2_SECRET_KEY=
|
|
|
2016
2125
|
`;
|
|
2017
2126
|
fs.writeFileSync(path.join(backendDest, '.env'), envContent, 'utf-8');
|
|
2018
2127
|
fs.writeFileSync(path.join(backendDest, '.env.example'), envContent, 'utf-8');
|
|
2128
|
+
if (isMicroservices)
|
|
2129
|
+
prepareStatelessGateway(backendDest);
|
|
2019
2130
|
}
|
|
2020
2131
|
if (!fs.existsSync(frontendDest) || fs.readdirSync(frontendDest).length === 0) {
|
|
2021
2132
|
console.log(`Copiando boilerplate do frontend para: ${frontendDest}...`);
|
|
@@ -2345,4 +2456,72 @@ export class Devkit${pascalName}Component {}
|
|
|
2345
2456
|
console.log(`[UI] Componente customizado do DevKit "devkit-${componentName}" criado com sucesso em: ${customDir}`);
|
|
2346
2457
|
}
|
|
2347
2458
|
});
|
|
2348
|
-
program
|
|
2459
|
+
program
|
|
2460
|
+
.command('entity:set-module <entityName> <moduleName>')
|
|
2461
|
+
.description('Planeja ou aplica a movimentação segura de uma entidade entre módulos funcionais')
|
|
2462
|
+
.option('--dry-run', 'Exibe o plano sem alterar arquivos (padrão)')
|
|
2463
|
+
.option('--apply', 'Aplica o plano monolítico quando não há conflitos')
|
|
2464
|
+
.action((entityName, moduleName, options) => {
|
|
2465
|
+
const projectRoot = process.cwd();
|
|
2466
|
+
const configPath = path.join(projectRoot, 'devstroupe.config.ts');
|
|
2467
|
+
if (!fs.existsSync(configPath))
|
|
2468
|
+
throw new Error('devstroupe.config.ts não encontrado.');
|
|
2469
|
+
const imported = require(configPath);
|
|
2470
|
+
const config = imported.default ?? imported;
|
|
2471
|
+
const plan = (0, entity_module_move_1.planEntityModuleMove)(projectRoot, config, entityName, moduleName);
|
|
2472
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
2473
|
+
if (!options.apply) {
|
|
2474
|
+
console.log('[Dry-run] Nenhum arquivo foi alterado. Revise o plano e use --apply para aprovar.');
|
|
2475
|
+
return;
|
|
2476
|
+
}
|
|
2477
|
+
if (plan.conflicts.length) {
|
|
2478
|
+
throw new Error(`Movimentação não aplicada:\n- ${plan.conflicts.join('\n- ')}`);
|
|
2479
|
+
}
|
|
2480
|
+
if (plan.backendType !== 'monolith') {
|
|
2481
|
+
throw new Error('Mudanças de ownership entre bancos exigem plano de dados explícito e não são aplicadas automaticamente.');
|
|
2482
|
+
}
|
|
2483
|
+
const configSource = fs.readFileSync(configPath, 'utf-8');
|
|
2484
|
+
const backupPath = `${configPath}.devstroupe-backup`;
|
|
2485
|
+
if (!fs.existsSync(backupPath))
|
|
2486
|
+
fs.copyFileSync(configPath, backupPath);
|
|
2487
|
+
fs.writeFileSync(configPath, (0, entity_module_move_1.updateEntityModuleInConfig)(configSource, entityName, plan.targetModule), 'utf-8');
|
|
2488
|
+
const entity = config.entities.find((candidate) => candidate.name === entityName);
|
|
2489
|
+
entity.module = plan.targetModule;
|
|
2490
|
+
const move = plan.operations.find((operation) => operation.kind === 'move');
|
|
2491
|
+
fs.ensureDirSync(path.dirname(move.to));
|
|
2492
|
+
fs.moveSync(move.from, move.to, { overwrite: false });
|
|
2493
|
+
const movedController = path.join(move.to, 'api', `${entityName}.controller.ts`);
|
|
2494
|
+
if (fs.existsSync(movedController)) {
|
|
2495
|
+
const source = fs.readFileSync(movedController, 'utf-8');
|
|
2496
|
+
fs.writeFileSync(movedController, source.replace(/@Controller\(['"][^'"]+['"]\)/, `@Controller('${plan.targetModule}/${entityName}')`), 'utf-8');
|
|
2497
|
+
}
|
|
2498
|
+
const backendRoot = path.resolve(projectRoot, config.backendPath ?? './backend');
|
|
2499
|
+
for (const functionalModule of [plan.sourceModule, plan.targetModule]) {
|
|
2500
|
+
const moduleRoot = path.join(backendRoot, 'src', 'modules', functionalModule);
|
|
2501
|
+
const members = config.entities.filter((candidate) => (0, devkit_core_1.resolveEntityModule)(candidate) === functionalModule && generatedEntityReady(moduleRoot, candidate.name));
|
|
2502
|
+
const moduleFile = path.join(moduleRoot, `${functionalModule}.module.ts`);
|
|
2503
|
+
if (members.length || functionalModule === 'core') {
|
|
2504
|
+
fs.writeFileSync(moduleFile, (0, functional_module_templates_1.nestFunctionalModuleTemplate)(functionalModule, members), 'utf-8');
|
|
2505
|
+
patchMonolithAppModule(path.join(backendRoot, 'src', 'app.module.ts'), functionalModule);
|
|
2506
|
+
}
|
|
2507
|
+
else {
|
|
2508
|
+
if (fs.existsSync(moduleFile))
|
|
2509
|
+
fs.removeSync(moduleFile);
|
|
2510
|
+
removeMonolithModuleImport(path.join(backendRoot, 'src', 'app.module.ts'), functionalModule);
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
const migrationsRoot = path.join(backendRoot, 'src', 'database', 'migrations');
|
|
2514
|
+
if (fs.existsSync(migrationsRoot)) {
|
|
2515
|
+
for (const file of fs.readdirSync(migrationsRoot).filter((candidate) => candidate.endsWith(`-create-${entityName}.ts`))) {
|
|
2516
|
+
const migrationPath = path.join(migrationsRoot, file);
|
|
2517
|
+
const source = fs.readFileSync(migrationPath, 'utf-8');
|
|
2518
|
+
fs.writeFileSync(migrationPath, source.match(/^\/\/ owner-module:/)
|
|
2519
|
+
? source.replace(/^\/\/ owner-module:.*$/m, `// owner-module: ${plan.targetModule}`)
|
|
2520
|
+
: `// owner-module: ${plan.targetModule}\n${source}`, 'utf-8');
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
console.log(`[Aplicado] ${entityName}: ${plan.sourceModule} -> ${plan.targetModule}. Backup: ${backupPath}`);
|
|
2524
|
+
});
|
|
2525
|
+
if (require.main === module) {
|
|
2526
|
+
program.parse(process.argv);
|
|
2527
|
+
}
|