Files
wwjcloud/tools/module-generator.js

581 lines
16 KiB
JavaScript
Raw Normal View History

const fs = require('fs');
const path = require('path');
/**
* NestJS模块生成器
* 为每个模块创建对应的.module.ts文件并正确引用所有组件
*/
class ModuleGenerator {
constructor() {
this.config = {
nestjsBasePath: '/Users/wanwu/Documents/wwjcloud/wwjcloud-nsetjs/wwjcloud/src/common',
discoveryResultPath: './tools/php-discovery-result.json'
};
this.discoveryData = null;
this.stats = {
createdModules: 0,
updatedModules: 0,
errors: 0
};
}
/**
* 运行模块生成
*/
async run() {
try {
console.log('🚀 启动NestJS模块生成器...');
console.log('目标:为每个模块创建.module.ts文件并正确引用所有组件\n');
// 第1阶段加载PHP文件发现结果
console.log('📊 第1阶段加载PHP文件发现结果...');
await this.loadDiscoveryData();
console.log(' ✅ 成功加载PHP文件发现结果');
// 第2阶段扫描现有文件结构
console.log('\n📊 第2阶段扫描现有文件结构...');
const moduleStructure = await this.scanModuleStructure();
console.log(` ✅ 扫描了 ${Object.keys(moduleStructure).length} 个模块`);
// 第3阶段生成模块文件
console.log('\n📊 第3阶段生成模块文件...');
await this.generateModules(moduleStructure);
console.log(` ✅ 生成了 ${this.stats.createdModules} 个模块文件`);
// 第4阶段生成统计报告
console.log('\n📊 第4阶段生成统计报告...');
this.generateStatsReport();
} catch (error) {
console.error('❌ 生成过程中发生错误:', error.message);
this.stats.errors++;
throw error;
}
}
/**
* 加载PHP文件发现结果
*/
async loadDiscoveryData() {
try {
const data = fs.readFileSync(this.config.discoveryResultPath, 'utf8');
this.discoveryData = JSON.parse(data);
} catch (error) {
throw new Error(`无法加载发现结果文件: ${error.message}`);
}
}
/**
* 扫描模块结构
*/
async scanModuleStructure() {
const moduleStructure = {};
const commonPath = this.config.nestjsBasePath;
if (!fs.existsSync(commonPath)) {
console.log(' ⚠️ common目录不存在');
return moduleStructure;
}
const modules = fs.readdirSync(commonPath, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
for (const moduleName of modules) {
const modulePath = path.join(commonPath, moduleName);
moduleStructure[moduleName] = {
controllers: this.scanControllers(modulePath),
services: this.scanServices(modulePath),
entities: this.scanEntities(modulePath),
validators: this.scanValidators(modulePath),
middlewares: this.scanMiddlewares(modulePath),
jobs: this.scanJobs(modulePath),
listeners: this.scanListeners(modulePath),
commands: this.scanCommands(modulePath),
dicts: this.scanDicts(modulePath)
};
}
return moduleStructure;
}
/**
* 读取实际文件中的类名
*/
getActualClassName(filePath) {
try {
if (!fs.existsSync(filePath)) {
return null;
}
const content = fs.readFileSync(filePath, 'utf8');
const match = content.match(/export\s+(?:class|interface|enum)\s+(\w+)/);
return match ? match[1] : null;
} catch (error) {
console.error(`读取文件 ${filePath} 时出错:`, error.message);
return null;
}
}
/**
* 扫描控制器
*/
scanControllers(modulePath) {
const controllers = [];
const controllersPath = path.join(modulePath, 'controllers');
if (fs.existsSync(controllersPath)) {
const layers = ['adminapi', 'api'];
for (const layer of layers) {
const layerPath = path.join(controllersPath, layer);
if (fs.existsSync(layerPath)) {
const allFiles = fs.readdirSync(layerPath);
const controllerFiles = allFiles.filter(file => file.endsWith('Controller.ts'));
if (controllerFiles.length > 0) {
console.log(` 发现 ${layer} 层控制器: ${controllerFiles.join(', ')}`);
}
const files = controllerFiles.map(file => {
const filePath = path.join(layerPath, file);
const actualClassName = this.getActualClassName(filePath);
return {
name: actualClassName || file.replace('Controller.ts', ''),
path: `./controllers/${layer}/${file}`,
layer: layer
};
});
controllers.push(...files);
}
}
}
return controllers;
}
/**
* 扫描服务
*/
scanServices(modulePath) {
const services = [];
const servicesPath = path.join(modulePath, 'services');
if (fs.existsSync(servicesPath)) {
const layers = ['admin', 'api', 'core'];
for (const layer of layers) {
const layerPath = path.join(servicesPath, layer);
if (fs.existsSync(layerPath)) {
const files = fs.readdirSync(layerPath)
.filter(file => file.endsWith('.service.ts'))
.map(file => {
const filePath = path.join(layerPath, file);
const actualClassName = this.getActualClassName(filePath);
return {
name: actualClassName || file.replace('.service.ts', ''),
path: `./services/${layer}/${file}`,
layer: layer
};
});
services.push(...files);
}
}
}
return services;
}
/**
* 扫描实体
*/
scanEntities(modulePath) {
const entities = [];
const entitiesPath = path.join(modulePath, 'entity');
if (fs.existsSync(entitiesPath)) {
const files = fs.readdirSync(entitiesPath)
.filter(file => file.endsWith('.ts') && !file.endsWith('.d.ts'))
.map(file => ({
name: file.replace('.ts', ''),
path: `./entity/${file}`
}));
entities.push(...files);
}
return entities;
}
/**
* 扫描验证器
*/
scanValidators(modulePath) {
const validators = [];
const validatorsPath = path.join(modulePath, 'dto');
if (fs.existsSync(validatorsPath)) {
const files = fs.readdirSync(validatorsPath, { recursive: true })
.filter(file => file.endsWith('.ts') && !file.endsWith('.d.ts'))
.map(file => ({
name: file.replace('.ts', ''),
path: `./dto/${file}`
}));
validators.push(...files);
}
return validators;
}
/**
* 扫描中间件
*/
scanMiddlewares(modulePath) {
const middlewares = [];
const middlewaresPath = path.join(modulePath, 'guards');
if (fs.existsSync(middlewaresPath)) {
const files = fs.readdirSync(middlewaresPath)
.filter(file => file.endsWith('.ts') && !file.endsWith('.d.ts'))
.map(file => ({
name: file.replace('.ts', ''),
path: `./guards/${file}`
}));
middlewares.push(...files);
}
return middlewares;
}
/**
* 扫描任务
*/
scanJobs(modulePath) {
const jobs = [];
const jobsPath = path.join(modulePath, 'jobs');
if (fs.existsSync(jobsPath)) {
const files = fs.readdirSync(jobsPath)
.filter(file => file.endsWith('.ts') && !file.endsWith('.d.ts'))
.map(file => ({
name: file.replace('.ts', ''),
path: `./jobs/${file}`
}));
jobs.push(...files);
}
return jobs;
}
/**
* 扫描监听器
*/
scanListeners(modulePath) {
const listeners = [];
const listenersPath = path.join(modulePath, 'listeners');
if (fs.existsSync(listenersPath)) {
const files = fs.readdirSync(listenersPath)
.filter(file => file.endsWith('.ts') && !file.endsWith('.d.ts'))
.map(file => ({
name: file.replace('.ts', ''),
path: `./listeners/${file}`
}));
listeners.push(...files);
}
return listeners;
}
/**
* 扫描命令
*/
scanCommands(modulePath) {
const commands = [];
const commandsPath = path.join(modulePath, 'commands');
if (fs.existsSync(commandsPath)) {
const files = fs.readdirSync(commandsPath)
.filter(file => file.endsWith('.ts') && !file.endsWith('.d.ts'))
.map(file => ({
name: file.replace('.ts', ''),
path: `./commands/${file}`
}));
commands.push(...files);
}
return commands;
}
/**
* 扫描字典
*/
scanDicts(modulePath) {
const dicts = [];
const dictsPath = path.join(modulePath, 'dicts');
if (fs.existsSync(dictsPath)) {
const files = fs.readdirSync(dictsPath)
.filter(file => file.endsWith('.ts') && !file.endsWith('.d.ts'))
.map(file => ({
name: file.replace('.ts', ''),
path: `./dicts/${file}`
}));
dicts.push(...files);
}
return dicts;
}
/**
* 生成模块文件
*/
async generateModules(moduleStructure) {
console.log(' 🔨 生成模块文件...');
for (const [moduleName, components] of Object.entries(moduleStructure)) {
try {
await this.generateModuleFile(moduleName, components);
this.stats.createdModules++;
} catch (error) {
console.error(` ❌ 生成模块 ${moduleName} 失败:`, error.message);
this.stats.errors++;
}
}
}
/**
* 生成单个模块文件
*/
async generateModuleFile(moduleName, components) {
const modulePath = path.join(this.config.nestjsBasePath, moduleName, `${moduleName}.module.ts`);
// 生成模块内容
const moduleContent = this.generateModuleContent(moduleName, components);
// 确保目录存在
this.ensureDir(path.dirname(modulePath));
// 写入文件
fs.writeFileSync(modulePath, moduleContent);
console.log(` ✅ 创建模块: ${moduleName}/${moduleName}.module.ts`);
}
/**
* 生成模块内容
*/
generateModuleContent(moduleName, components) {
const className = this.toPascalCase(moduleName);
let imports = [];
let controllers = [];
let providers = [];
let exports = [];
let importSet = new Set(); // 用于去重
// 导入控制器
for (const controller of components.controllers) {
const importName = this.toPascalCase(controller.name);
const cleanPath = controller.path.replace('.ts', '');
const importKey = `${importName}:${cleanPath}`;
if (!importSet.has(importKey)) {
imports.push(`import { ${importName} } from '${cleanPath}';`);
controllers.push(importName);
importSet.add(importKey);
}
}
// 导入服务
for (const service of components.services) {
const baseName = this.toPascalCase(service.name);
const layerPrefix = this.getLayerPrefix(service.layer, baseName);
const importName = layerPrefix ? `${layerPrefix}${baseName}` : baseName;
const cleanPath = service.path.replace('.ts', '');
const importKey = `${importName}:${cleanPath}`;
if (!importSet.has(importKey)) {
if (this.needsAlias(service.layer, baseName)) {
imports.push(`import { ${baseName} as ${importName} } from '${cleanPath}';`);
} else {
imports.push(`import { ${importName} } from '${cleanPath}';`);
}
providers.push(importName);
importSet.add(importKey);
}
}
// 导入实体
for (const entity of components.entities) {
const baseName = this.toPascalCase(entity.name);
const importName = `Entity${baseName}`;
const cleanPath = entity.path.replace('.ts', '');
const importKey = `${importName}:${cleanPath}`;
if (!importSet.has(importKey)) {
imports.push(`import { ${baseName} as ${importName} } from '${cleanPath}';`);
providers.push(importName);
importSet.add(importKey);
}
}
// 导入验证器
for (const validator of components.validators) {
const baseName = this.toPascalCase(validator.name);
const importName = `Validator${baseName}`;
const cleanPath = validator.path.replace('.ts', '');
const importKey = `${importName}:${cleanPath}`;
if (!importSet.has(importKey)) {
imports.push(`import { ${baseName} as ${importName} } from '${cleanPath}';`);
providers.push(importName);
importSet.add(importKey);
}
}
// 导入中间件
for (const middleware of components.middlewares) {
const importName = this.toPascalCase(middleware.name);
const cleanPath = middleware.path.replace('.ts', '');
const importKey = `${importName}:${cleanPath}`;
if (!importSet.has(importKey)) {
imports.push(`import { ${importName} } from '${cleanPath}';`);
providers.push(importName);
importSet.add(importKey);
}
}
// 导入任务
for (const job of components.jobs) {
const importName = this.toPascalCase(job.name);
const cleanPath = job.path.replace('.ts', '');
const importKey = `${importName}:${cleanPath}`;
if (!importSet.has(importKey)) {
imports.push(`import { ${importName} } from '${cleanPath}';`);
providers.push(importName);
importSet.add(importKey);
}
}
// 导入监听器
for (const listener of components.listeners) {
const importName = this.toPascalCase(listener.name);
const cleanPath = listener.path.replace('.ts', '');
const importKey = `${importName}:${cleanPath}`;
if (!importSet.has(importKey)) {
imports.push(`import { ${importName} } from '${cleanPath}';`);
providers.push(importName);
importSet.add(importKey);
}
}
// 导入命令
for (const command of components.commands) {
const importName = this.toPascalCase(command.name);
const cleanPath = command.path.replace('.ts', '');
const importKey = `${importName}:${cleanPath}`;
if (!importSet.has(importKey)) {
imports.push(`import { ${importName} } from '${cleanPath}';`);
providers.push(importName);
importSet.add(importKey);
}
}
// 导入字典
for (const dict of components.dicts) {
const importName = this.toPascalCase(dict.name);
const cleanPath = dict.path.replace('.ts', '');
const importKey = `${importName}:${cleanPath}`;
if (!importSet.has(importKey)) {
imports.push(`import { ${importName} } from '${cleanPath}';`);
providers.push(importName);
importSet.add(importKey);
}
}
// 导出服务
exports.push(...providers);
const content = `import { Module } from '@nestjs/common';
${imports.join('\n')}
@Module({
controllers: [${controllers.join(', ')}],
providers: [${providers.join(', ')}],
exports: [${exports.join(', ')}],
})
export class ${className}Module {}
`;
return content;
}
/**
* 确保目录存在
*/
ensureDir(dirPath) {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
}
/**
* 转换为PascalCase
*/
toPascalCase(str) {
return str.replace(/(^|_)([a-z])/g, (match, p1, p2) => p2.toUpperCase());
}
/**
* 获取层前缀
*/
getLayerPrefix(layer, serviceName) {
// 如果服务名已经包含Core前缀则不需要再添加
if (layer === 'core' && serviceName.toLowerCase().startsWith('core')) {
return '';
}
const layerMap = {
'admin': 'Admin',
'api': 'Api',
'core': 'Core'
};
return layerMap[layer] || '';
}
/**
* 检查是否需要别名
*/
needsAlias(layer, serviceName) {
// 如果服务名已经包含层前缀,则不需要别名
if (layer === 'core' && serviceName.toLowerCase().startsWith('core')) {
return false;
}
return true;
}
/**
* 生成统计报告
*/
generateStatsReport() {
console.log('\n📊 NestJS模块生成统计报告:');
console.log('============================================================');
console.log(` 📁 创建模块: ${this.stats.createdModules}`);
console.log(` 🔄 更新模块: ${this.stats.updatedModules}`);
console.log(` ❌ 错误数量: ${this.stats.errors}`);
console.log('============================================================');
console.log('\n✅ 🎉 NestJS模块生成完成');
}
}
// 运行模块生成器
if (require.main === module) {
const generator = new ModuleGenerator();
generator.run().catch(console.error);
}
module.exports = ModuleGenerator;