Files
wwjcloud-nest-v1/wwjcloud-nest-v1/tools/tools-uni/utils/analysis-utils.js
wanwujie 0f105d3a21 🎯 重构目录结构:完成项目组织优化
- 将wwjcloud目录重命名为wwjcloud-nest-v1作为项目根目录
- 将原nestjs目录重命名为wwjcloud作为NestJS后端目录
- 实现真正的前后端分离架构
- 恢复工作区中丢失的目录结构
- 更新相关配置文件路径引用
- 清理重复和嵌套目录问题

目录结构:
wwjcloud-nest-v1/
├── wwjcloud/          # NestJS 后端
├── admin/             # 管理端前端
├── web/               # PC端前端
├── uni-app-x/         # 移动端前端
├── wwjcloud-web/      # 部署根目录
├── docker/            # Docker 配置
├── docs/              # 文档
└── tools/             # 工具集
2025-10-21 13:38:58 +08:00

514 lines
16 KiB
JavaScript

#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
/**
* 代码分析工具
* 用于分析Java框架uni-app项目的结构和依赖关系
*/
class AnalysisUtils {
constructor() {
this.dependencies = new Map();
this.components = new Map();
this.pages = new Map();
this.hooks = new Map();
this.stores = new Map();
}
/**
* 分析整个项目结构
*/
async analyzeProject(projectPath) {
console.log(`🔍 开始分析项目: ${projectPath}`);
if (!fs.existsSync(projectPath)) {
throw new Error(`项目路径不存在: ${projectPath}`);
}
const analysis = {
basic: await this.analyzeBasicInfo(projectPath),
dependencies: await this.analyzeDependencies(projectPath),
structure: await this.analyzeStructure(projectPath),
components: await this.analyzeComponents(projectPath),
pages: await this.analyzePages(projectPath),
hooks: await this.analyzeHooks(projectPath),
styles: await this.analyzeStyles(projectPath)
};
console.log('✅ 项目分析完成');
return analysis;
}
/**
* 分析基础信息
*/
async analyzeBasicInfo(projectPath) {
const info = {
name: '',
version: '',
uniAppVersion: '',
vueVersion: '',
platform: '',
buildTool: ''
};
// 读取package.json
const packageJsonPath = path.join(projectPath, 'package.json');
if (fs.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
info.name = packageJson.name || '';
info.version = packageJson.version || '';
info.uniAppVersion = packageJson.dependencies?.['@dcloudio/uni-app'] || '';
info.vueVersion = packageJson.dependencies?.vue || '';
// 检查构建工具
if (packageJson.devDependencies?.vite) {
info.buildTool = 'Vite';
} else if (packageJson.devDependencies?.webpack) {
info.buildTool = 'Webpack';
}
}
// 读取manifest.json确定平台
const manifestPath = path.join(projectPath, 'src/manifest.json');
if (fs.existsSync(manifestPath)) {
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
if (manifest['app-plus']) info.platform += 'App ';
if (manifest['mp-weixin']) info.platform += '微信小程序 ';
if (manifest['h5']) info.platform += 'H5 ';
}
return info;
}
/**
* 分析依赖关系
*/
async analyzeDependencies(projectPath) {
const dependencies = new Map();
const packageJsonPath = path.join(projectPath, 'package.json');
if (fs.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
// 分析核心依赖
const coreDeps = packageJson.dependencies || {};
const devDeps = packageJson.devDependencies || {};
// uni-app相关依赖
Object.keys(coreDeps).forEach(dep => {
if (dep.includes('@dcloudio') || dep.includes('uni')) {
dependencies.set(dep, {
version: coreDeps[dep],
type: 'uni-app',
category: this.categorizeDependency(dep)
});
}
});
// Vue相关依赖
if (coreDeps.vue) {
dependencies.set('vue', {
version: coreDeps.vue,
type: 'framework',
category: 'core'
});
}
// UI组件库
['uview-plus', 'uni-ui', '@nutui/nutui'].forEach(uiLib => {
if (coreDeps[uiLib]) {
dependencies.set(uiLib, {
version: coreDeps[uiLib],
type: 'ui-library',
category: 'ui'
});
}
});
// 状态管理
['pinia', 'vuex'].forEach(stateLib => {
if (coreDeps[stateLib]) {
dependencies.set(stateLib, {
version: coreDeps[stateLib],
type: 'state-management',
category: 'state'
});
}
});
}
return dependencies;
}
/**
* 分析项目结构
*/
async analyzeStructure(projectPath) {
const structure = {
hasPages: false,
hasComponents: false,
hasUtils: false,
hasStores: false,
hasStyles: false,
directories: []
};
const srcPath = path.join(projectPath, 'src');
if (!fs.existsSync(srcPath)) return structure;
const items = fs.readdirSync(srcPath, { withFileTypes: true });
for (const item of items) {
if (item.isDirectory()) {
structure.directories.push(item.name);
switch (item.name) {
case 'pages':
case 'app':
structure.hasPages = true;
break;
case 'components':
case 'app/components':
structure.hasComponents = true;
break;
case 'utils':
structure.hasUtils = true;
break;
case 'stores':
case 'app/stores':
structure.hasStores = true;
break;
case 'styles':
structure.hasStyles = true;
break;
}
}
}
return structure;
}
/**
* 分析组件
*/
async analyzeComponents(projectPath) {
const components = new Map();
const componentPaths = [
path.join(projectPath, 'src/components'),
path.join(projectPath, 'src/app/components'),
path.join(projectPath, 'src/addon/components')
];
for (const componentPath of componentPaths) {
if (fs.existsSync(componentPath)) {
await this.analyzeComponentDirectory(componentPath, components);
}
}
return components;
}
/**
* 分析组件目录
*/
async analyzeComponentDirectory(dirPath, components) {
const items = fs.readdirSync(dirPath, { withFileTypes: true });
for (const item of items) {
const fullPath = path.join(dirPath, item.name);
if (item.isDirectory()) {
await this.analyzeComponentDirectory(fullPath, components);
} else if (item.name.endsWith('.vue')) {
const componentInfo = await this.analyzeVueFile(fullPath);
components.set(item.name.replace('.vue', ''), componentInfo);
}
}
}
/**
* 分析Vue文件
*/
async analyzeVueFile(filePath) {
const content = fs.readFileSync(filePath, 'utf-8');
return {
path: filePath,
hasTemplate: content.includes('<template>'),
hasScript: content.includes('<script'),
hasStyle: content.includes('<style'),
scriptType: this.extractScriptType(content),
components: this.extractComponentImports(content),
hasCompositionApi: content.includes('setup'),
hasOptionsApi: content.includes('export default') && !content.includes('setup'),
styleLang: this.extractStyleLang(content)
};
}
/**
* 分析页面
*/
async analyzePages(projectPath) {
const pages = new Map();
const pagePaths = [
path.join(projectPath, 'src/pages'),
path.join(projectPath, 'src/app/pages')
];
for (const pagePath of pagePaths) {
if (fs.existsSync(pagePath)) {
await this.analyzePageDirectory(pagePath, pages);
}
}
return pages;
}
/**
* 分析页面目录
*/
async analyzePageDirectory(dirPath, pages) {
const items = fs.readdirSync(dirPath, { withFileTypes: true });
for (const item of items) {
const fullPath = path.join(dirPath, item.name);
if (item.isDirectory()) {
await this.analyzePageDirectory(fullPath, pages);
} else if (item.name.endsWith('.vue')) {
const pageInfo = await this.analyzeVueFile(fullPath);
pageInfo.isPage = true;
pages.set(item.name.replace('.vue', ''), pageInfo);
}
}
}
/**
* 分析Hooks
*/
async analyzeHooks(projectPath) {
const hooks = new Map();
const hookPaths = [
path.join(projectPath, 'src/hooks'),
path.join(projectPath, 'src/app/hooks')
];
for (const hookPath of hookPaths) {
if (fs.existsSync(hookPath)) {
const files = fs.readdirSync(hookPath);
for (const file of files) {
if (file.endsWith('.ts') || file.endsWith('.js')) {
const content = fs.readFileSync(path.join(hookPath, file), 'utf-8');
hooks.set(file.replace(/\.(ts|js)$/, ''), {
path: path.join(hookPath, file),
exports: this.extractExports(content),
imports: this.extractImports(content),
hasTypes: file.endsWith('.ts')
});
}
}
}
}
return hooks;
}
/**
* 分析样式文件
*/
async analyzeStyles(projectPath) {
const styles = {
global: [],
components: [],
hasScss: false,
hasSass: false,
hasLess: false,
hasWindiCSS: false
};
const stylePaths = [
path.join(projectPath, 'src/styles'),
path.join(projectPath, 'src')
];
for (const stylePath of stylePaths) {
if (fs.existsSync(stylePath)) {
await this.analyzeStyleDirectory(stylePath, styles);
}
}
// 检查构建配置
const viteConfigPath = path.join(projectPath, 'vite.config.ts');
if (fs.existsSync(viteConfigPath)) {
const content = fs.readFileSync(viteConfigPath, 'utf-8');
styles.hasWindiCSS = content.includes('windicss') || content.includes('WindiCSS');
}
return styles;
}
/**
* 分析样式目录
*/
async analyzeStyleDirectory(dirPath, styles) {
const items = fs.readdirSync(dirPath, { withFileTypes: true });
for (const item of items) {
const fullPath = path.join(dirPath, item.name);
if (item.isDirectory()) {
await this.analyzeStyleDirectory(fullPath, styles);
} else if (this.isStyleFile(item.name)) {
styles.global.push({
path: fullPath,
type: this.getStyleFileType(item.name),
size: fs.statSync(fullPath).size
});
// 检查样式类型
if (item.name.endsWith('.scss') || item.name.endsWith('.sass')) {
styles.hasScss = true;
}
if (item.name.endsWith('.less')) {
styles.hasLess = true;
}
}
}
}
/**
* 分类依赖
*/
categorizeDependency(depName) {
if (depName.includes('uni-app')) return 'core';
if (depName.includes('uni-mp')) return 'miniprogram';
if (depName.includes('uni-h5')) return 'h5';
if (depName.includes('uni-components')) return 'components';
return 'other';
}
/**
* 提取脚本类型
*/
extractScriptType(content) {
if (content.match(/<script[^>]*setup[^>]*>/)) return 'setup';
if (content.match(/<script[^>]*lang=['"]ts['"]/)) return 'typescript';
if (content.match(/<script/)) return 'javascript';
return 'none';
}
/**
* 提取组件导入
*/
extractComponentImports(content) {
const imports = [];
const match = content.match(/import\s+(.+?)\s+from\s+['"](.+?)['"];?/g);
if (match) {
match.forEach(imp => {
imports.push(imp.trim());
});
}
return imports;
}
/**
* 提取样式语言
*/
extractStyleLang(content) {
const match = content.match(/<style[^>]*lang=['"]([^'"]+)['"]/);
return match ? match[1] : 'css';
}
/**
* 提取导出
*/
extractExports(content) {
const exports = [];
const match = content.match(/export\s+(.+?);?/g);
if (match) {
match.forEach(exp => {
exports.push(exp.trim());
});
}
return exports;
}
/**
* 提取导入
*/
extractImports(content) {
const imports = [];
const match = content.match(/import\s+(.+?)\s+from\s+['"](.+?)['"];?/g);
if (match) {
match.forEach(imp => {
imports.push(imp.trim());
});
}
return imports;
}
/**
* 检查是否为样式文件
*/
isStyleFile(fileName) {
const styleExtensions = ['.css', '.scss', '.sass', '.less', '.styl'];
return styleExtensions.some(ext => fileName.endsWith(ext));
}
/**
* 获取样式文件类型
*/
getStyleFileType(fileName) {
if (fileName.endsWith('.scss')) return 'scss';
if (fileName.endsWith('.sass')) return 'sass';
if (fileName.endsWith('.less')) return 'less';
if (fileName.endsWith('.styl')) return 'stylus';
return 'css';
}
/**
* 生成分析报告
*/
generateReport(analysis) {
const report = {
summary: {
totalComponents: analysis.components.size,
totalPages: analysis.pages.size,
totalHooks: analysis.hooks.size,
hasTypeScript: Object.values(analysis.basic).some(v => v.includes('typescript')),
hasCompositionApi: false,
hasOptionsApi: false
},
recommendations: []
};
// 分析API使用情况
for (const [name, component] of analysis.components) {
if (component.hasCompositionApi) report.summary.hasCompositionApi = true;
if (component.hasOptionsApi) report.summary.hasOptionsApi = true;
}
for (const [name, page] of analysis.pages) {
if (page.hasCompositionApi) report.summary.hasCompositionApi = true;
if (page.hasOptionsApi) report.summary.hasOptionsApi = true;
}
// 生成建议
if (analysis.basic.uniAppVersion && !analysis.basic.uniAppVersion.includes('x')) {
report.recommendations.push('建议升级到uni-app x以获得更好的性能和原生体验');
}
if (report.summary.hasOptionsApi && !report.summary.hasCompositionApi) {
report.recommendations.push('建议迁移到Composition API以获得更好的TypeScript支持');
}
return report;
}
}
module.exports = AnalysisUtils;