| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- #!/usr/bin/env node
- /**
- * 静态方法 Import 替换工具
- *
- * 功能:将 antd 静态方法的 import 替换为全局单例
- * 使用方法:node replace-static-import.js <目标目录> [--dry-run] [--backup]
- *
- * 替换规则:
- * - import { message } from 'antd' → import { globalMessage as message } from '@/utils/antd-global'
- * - import { notification } from 'antd' → import { globalNotification as notification } from '@/utils/antd-global'
- * - import { Modal } from 'antd' → import { globalModal as Modal } from '@/utils/antd-global'
- */
- const fs = require('fs');
- const path = require('path');
- // 统计信息
- const stats = {
- totalFiles: 0,
- scannedFiles: 0,
- modifiedFiles: 0,
- replacements: {
- message: 0,
- notification: 0,
- Modal: 0,
- },
- errors: [],
- };
- // 替换详情
- const replacementDetails = [];
- /**
- * 解析 import 语句,提取导入的模块
- */
- function parseImports(content) {
- // 匹配 import { ... } from 'antd'
- const importRegex = /import\s+{([^}]+)}\s+from\s+['"]antd['"]/g;
- const imports = [];
-
- let match;
- while ((match = importRegex.exec(content)) !== null) {
- const fullMatch = match[0];
- const importedItems = match[1]
- .split(',')
- .map(item => item.trim())
- .filter(item => item.length > 0);
-
- imports.push({
- fullMatch,
- start: match.index,
- end: match.index + fullMatch.length,
- items: importedItems,
- });
- }
-
- return imports;
- }
- /**
- * 生成新的 import 语句
- */
- function generateNewImports(importedItems) {
- const targetItems = ['message', 'notification', 'Modal'];
-
- // 分离需要替换的和不需要替换的
- const needReplace = [];
- const keepOriginal = [];
-
- importedItems.forEach(item => {
- // 处理 as 别名的情况: message as msg
- const baseItem = item.split(' as ')[0].trim();
-
- if (targetItems.includes(baseItem)) {
- needReplace.push(item);
- } else {
- keepOriginal.push(item);
- }
- });
-
- const newImports = [];
-
- // 如果有不需要替换的,保留原 import
- if (keepOriginal.length > 0) {
- newImports.push(`import { ${keepOriginal.join(', ')} } from 'antd'`);
- }
-
- // 添加全局单例的 import
- if (needReplace.length > 0) {
- const globalImports = needReplace.map(item => {
- const parts = item.split(' as ');
- const baseItem = parts[0].trim();
- const alias = parts[1] ? parts[1].trim() : baseItem;
-
- const mapping = {
- 'message': 'globalMessage',
- 'notification': 'globalNotification',
- 'Modal': 'globalModal',
- };
-
- return `${mapping[baseItem]} as ${alias}`;
- });
-
- newImports.push(`import { ${globalImports.join(', ')} } from '@/utils/antd-global'`);
- }
-
- return {
- newImports,
- needReplace: needReplace.map(item => item.split(' as ')[0].trim()),
- };
- }
- /**
- * 处理单个文件
- */
- function processFile(filePath, options) {
- let content = fs.readFileSync(filePath, 'utf-8');
- const originalContent = content;
-
- // 解析 import
- const imports = parseImports(content);
-
- if (imports.length === 0) {
- return; // 没有 antd import,跳过
- }
-
- let modified = false;
- const fileReplacements = {
- message: false,
- notification: false,
- Modal: false,
- };
-
- // 从后往前替换,避免索引变化
- imports.reverse().forEach(importInfo => {
- const { fullMatch, start, end, items } = importInfo;
-
- const { newImports, needReplace } = generateNewImports(items);
-
- if (needReplace.length > 0) {
- // 记录替换项
- needReplace.forEach(item => {
- fileReplacements[item] = true;
- stats.replacements[item]++;
- });
-
- // 替换 import 语句
- const before = content.substring(0, start);
- const after = content.substring(end);
- content = before + newImports.join('\n') + after;
-
- modified = true;
- }
- });
-
- if (modified) {
- stats.modifiedFiles++;
-
- replacementDetails.push({
- file: filePath,
- replacements: fileReplacements,
- });
-
- if (options.dryRun) {
- console.log(`\n📝 [DRY RUN] 将修改: ${filePath}`);
- if (fileReplacements.message) console.log(' ✓ message → globalMessage');
- if (fileReplacements.notification) console.log(' ✓ notification → globalNotification');
- if (fileReplacements.Modal) console.log(' ✓ Modal → globalModal');
- } else {
- // 备份
- if (options.backup) {
- fs.writeFileSync(filePath + '.bak', originalContent);
- }
-
- // 写入
- try {
- fs.writeFileSync(filePath, content, 'utf-8');
- console.log(`✅ 已修改: ${filePath}`);
- if (fileReplacements.message) console.log(' ✓ message → globalMessage');
- if (fileReplacements.notification) console.log(' ✓ notification → globalNotification');
- if (fileReplacements.Modal) console.log(' ✓ Modal → globalModal');
- } catch (error) {
- stats.errors.push({ file: filePath, error: error.message });
- console.error(`❌ 修改失败: ${filePath}`);
- console.error(` 错误: ${error.message}`);
- }
- }
- }
- }
- /**
- * 递归扫描目录
- */
- function scanDirectory(dir, options) {
- const files = fs.readdirSync(dir);
-
- files.forEach(file => {
- const filePath = path.join(dir, file);
- const stat = fs.statSync(filePath);
-
- if (stat.isDirectory()) {
- if (!['node_modules', '.git', 'build', 'dist', '.next'].includes(file)) {
- scanDirectory(filePath, options);
- }
- } else if (stat.isFile()) {
- if (/\.(tsx?|jsx?)$/.test(file)) {
- stats.totalFiles++;
- stats.scannedFiles++;
- processFile(filePath, options);
- }
- }
- });
- }
- /**
- * 生成报告
- */
- function generateReport(options) {
- console.log('\n' + '='.repeat(80));
- console.log('📊 静态方法 Import 替换报告');
- if (options.dryRun) {
- console.log(' [DRY RUN 模式 - 未实际修改文件]');
- }
- console.log('='.repeat(80) + '\n');
-
- console.log('📁 文件统计:');
- console.log(` 扫描文件总数: ${stats.totalFiles}`);
- console.log(` 修改文件数: ${stats.modifiedFiles}\n`);
-
- console.log('📋 替换统计:');
- console.log(` message: ${stats.replacements.message} 处`);
- console.log(` notification: ${stats.replacements.notification} 处`);
- console.log(` Modal: ${stats.replacements.Modal} 处`);
- console.log(` 总计: ${Object.values(stats.replacements).reduce((a, b) => a + b, 0)} 处\n`);
-
- if (stats.errors.length > 0) {
- console.log('❌ 错误列表:');
- stats.errors.forEach(({ file, error }) => {
- console.log(` ${file}`);
- console.log(` ${error}`);
- });
- console.log('');
- }
-
- console.log('='.repeat(80));
-
- if (options.dryRun) {
- console.log('💡 提示: 这是 DRY RUN 模式,文件未被实际修改');
- console.log(' 如果确认无误,请去掉 --dry-run 参数重新运行');
- } else {
- console.log('✅ 替换完成!');
- if (options.backup) {
- console.log(' 原文件已备份为 .bak 文件');
- }
- console.log(' 建议使用 git diff 检查改动');
- }
-
- console.log('='.repeat(80) + '\n');
-
- // 重要提醒
- if (stats.modifiedFiles > 0) {
- console.log('⚠️ 重要提醒:');
- console.log(' 1. 确保已创建 src/utils/antd-global.ts 文件');
- console.log(' 2. 确保 AppProvider 已初始化全局实例');
- console.log(' 3. 确保根组件包裹了 <App> 组件');
- console.log(' 4. 运行项目测试所有 message/notification/Modal 功能');
- console.log(' 5. 参考 static-methods-migration.md 了解详细说明\n');
-
- console.log('📝 下一步:');
- console.log(' 1. 检查代码改动: git diff');
- console.log(' 2. 运行项目: npm start');
- console.log(' 3. 测试静态方法是否正常工作');
- console.log(' 4. 如有问题,参考 static-methods-migration.md\n');
- }
- }
- /**
- * 生成 antd-global.ts 模板(如果文件不存在)
- */
- function checkAntdGlobalFile(projectRoot) {
- const targetPath = path.join(projectRoot, 'src/utils/antd-global.ts');
-
- if (fs.existsSync(targetPath)) {
- console.log(`✅ 已存在: ${targetPath}\n`);
- return;
- }
-
- console.log(`\n⚠️ 警告: 未找到 src/utils/antd-global.ts 文件`);
- console.log(' 这是全局单例工具的核心文件,必须先创建!\n');
- console.log('建议操作:');
- console.log(' 1. 运行: node migration-tools/component-template.js --global-utils ./src/utils');
- console.log(' 或');
- console.log(' 2. 参考 static-methods-migration.md 手动创建\n');
- }
- /**
- * 主函数
- */
- function main() {
- const args = process.argv.slice(2);
-
- if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
- console.log(`
- 静态方法 Import 替换工具
- 功能:
- 自动将 message/notification/Modal 的 import 替换为全局单例
- 用法:
- node replace-static-import.js <目标目录> [选项]
- 选项:
- --dry-run 只显示将要修改的内容,不实际修改文件
- --backup 修改前备份原文件(.bak)
- --help, -h 显示帮助信息
- 示例:
- # 预览将要修改的内容
- node replace-static-import.js ./src --dry-run
- # 执行替换并备份
- node replace-static-import.js ./src --backup
- 替换规则:
- import { message } from 'antd'
- → import { globalMessage as message } from '@/utils/antd-global'
- import { notification } from 'antd'
- → import { globalNotification as notification } from '@/utils/antd-global'
- import { Modal } from 'antd'
- → import { globalModal as Modal } from '@/utils/antd-global'
- 注意事项:
- 1. 必须先创建 src/utils/antd-global.ts 文件
- 2. 必须在 AppProvider 中初始化全局实例
- 3. 参考 static-methods-migration.md 了解详细说明
- `);
- process.exit(0);
- }
-
- const targetDir = args[0];
- const options = {
- dryRun: args.includes('--dry-run'),
- backup: args.includes('--backup'),
- };
-
- if (!fs.existsSync(targetDir)) {
- console.error(`❌ 目录不存在: ${targetDir}`);
- process.exit(1);
- }
-
- console.log(`\n🔧 开始处理目录: ${targetDir}`);
- if (options.dryRun) {
- console.log(' [DRY RUN 模式]');
- }
- if (options.backup) {
- console.log(' [备份模式]');
- }
-
- // 检查 antd-global.ts 文件
- const projectRoot = path.resolve(targetDir, '..');
- checkAntdGlobalFile(projectRoot);
-
- console.log('');
-
- scanDirectory(targetDir, options);
- generateReport(options);
- }
- main();
|