From 049c75e7f3f6f3488cdd5c33b4fe6d0ee7909b90 Mon Sep 17 00:00:00 2001 From: felix bear Date: Mon, 8 Jun 2026 19:09:45 +0900 Subject: [PATCH] refactor(scripts): share log migration helper #7917 (#8116) Co-authored-by: cocojojo5213 --- scripts/log-migration-helper.test.cjs | 55 +++++++++ scripts/log-migration-helper.ts | 171 ++++++++++++++++++++++++++ scripts/migrate-to-issue-log.ts | 150 ++-------------------- scripts/migrate-to-plugin-log.ts | 150 ++-------------------- 4 files changed, 240 insertions(+), 286 deletions(-) create mode 100644 scripts/log-migration-helper.test.cjs create mode 100644 scripts/log-migration-helper.ts diff --git a/scripts/log-migration-helper.test.cjs b/scripts/log-migration-helper.test.cjs new file mode 100644 index 0000000000..ebe1a963e4 --- /dev/null +++ b/scripts/log-migration-helper.test.cjs @@ -0,0 +1,55 @@ +require('ts-node/register/transpile-only'); + +const assert = require('node:assert/strict'); +const test = require('node:test'); +const { migrateLogContent } = require('./log-migration-helper.ts'); + +test('migrates Log calls and updates the import for the target logger', () => { + const input = [ + "import { Log, OtherLog } from '../core/log';", + '', + "Log.log('a');", + "Log.err('b');", + 'OtherLog.log();', + '', + ].join('\n'); + + const result = migrateLogContent(input, 'PluginLog'); + + assert.equal(result.changes, 2); + assert.equal( + result.content, + [ + "import { OtherLog, PluginLog } from '../core/log';", + '', + "PluginLog.log('a');", + "PluginLog.err('b');", + 'OtherLog.log();', + '', + ].join('\n'), + ); +}); + +test('keeps the Log import when non-call Log references remain', () => { + const input = [ + "import { Log } from '../core/log';", + '', + 'const logger = Log;', + "Log.info('a');", + '', + ].join('\n'); + + const result = migrateLogContent(input, 'IssueLog'); + + assert.equal(result.changes, 1); + assert.equal( + result.content, + [ + "import { Log, IssueLog } from '../core/log';", + '', + 'const logger = Log;', + "IssueLog.info('a');", + '', + ].join('\n'), + ); +}); diff --git a/scripts/log-migration-helper.ts b/scripts/log-migration-helper.ts new file mode 100644 index 0000000000..7bd8a5128e --- /dev/null +++ b/scripts/log-migration-helper.ts @@ -0,0 +1,171 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as glob from 'glob'; + +interface Replacement { + pattern: RegExp; + replacement: string; +} + +export interface LogMigrationConfig { + description: string; + fileLabel: string; + globPattern: string; + targetLogName: string; + ignore?: string[]; +} + +export interface MigrationResult { + content: string; + changes: number; +} + +export interface FileMigrationResult { + modified: boolean; + changes: number; +} + +const DEFAULT_IGNORE = ['**/*.spec.ts', '**/node_modules/**']; +const LOG_METHODS = ['log', 'err', 'info', 'debug', 'verbose', 'critical']; + +const createReplacements = (targetLogName: string): Replacement[] => + LOG_METHODS.map((method) => ({ + pattern: new RegExp(`\\bLog\\.${method}\\(`, 'g'), + replacement: `${targetLogName}.${method}(`, + })); + +const getTargetLogImportRegex = (targetLogName: string): RegExp => + new RegExp( + `import\\s*{[^}]*\\b${targetLogName}\\b[^}]*}\\s*from\\s*['"][^'"]*\\/log['"]`, + ); + +const LOG_IMPORT_REGEX = /import\s*{([^}]*\bLog\b[^}]*)}\s*from\s*(['"][^'"]*\/log['"])/; + +const updateImports = ( + content: string, + targetLogName: string, + replacements: Replacement[], +): string => { + if (getTargetLogImportRegex(targetLogName).test(content)) { + return content; + } + + const match = content.match(LOG_IMPORT_REGEX); + if (!match) { + return content; + } + + const [fullMatch, imports, importPath] = match; + const importList = imports.split(',').map((s) => s.trim()); + + if (!importList.includes(targetLogName)) { + importList.push(targetLogName); + } + + let tempContent = content; + for (const { pattern, replacement } of replacements) { + tempContent = tempContent.replace(pattern, replacement); + } + + tempContent = tempContent.replace(LOG_IMPORT_REGEX, ''); + + if (!/\bLog\b/.test(tempContent)) { + const logIndex = importList.indexOf('Log'); + if (logIndex > -1) { + importList.splice(logIndex, 1); + } + } + + return content.replace( + fullMatch, + `import { ${importList.join(', ')} } from ${importPath}`, + ); +}; + +export const migrateLogContent = ( + content: string, + targetLogName: string, +): MigrationResult => { + const replacements = createReplacements(targetLogName); + let nextContent = content; + let changeCount = 0; + + for (const { pattern, replacement } of replacements) { + const matches = nextContent.match(pattern); + if (matches) { + changeCount += matches.length; + nextContent = nextContent.replace(pattern, replacement); + } + } + + if (changeCount > 0) { + nextContent = updateImports(nextContent, targetLogName, replacements); + } + + return { + content: nextContent, + changes: changeCount, + }; +}; + +export const migrateLogFile = ( + filePath: string, + targetLogName: string, + dryRun: boolean = false, +): FileMigrationResult => { + try { + const originalContent = fs.readFileSync(filePath, 'utf8'); + const { content, changes } = migrateLogContent(originalContent, targetLogName); + const modified = content !== originalContent; + + if (modified && !dryRun) { + fs.writeFileSync(filePath, content, 'utf8'); + } + + return { modified, changes }; + } catch (error) { + console.error(`Error processing ${filePath}:`, error); + return { modified: false, changes: 0 }; + } +}; + +export const runLogMigration = (config: LogMigrationConfig): void => { + const dryRun = process.argv.includes('--dry-run'); + + console.log(`${config.description}\n`); + if (dryRun) { + console.log('Running in DRY RUN mode - no files will be modified\n'); + } + + const files = glob.sync(config.globPattern, { + ignore: config.ignore || DEFAULT_IGNORE, + absolute: true, + }); + + console.log(`Found ${files.length} ${config.fileLabel}\n`); + + const modifiedFiles: { path: string; changes: number }[] = []; + let totalChanges = 0; + + for (const file of files) { + const result = migrateLogFile(file, config.targetLogName, dryRun); + if (result.modified) { + modifiedFiles.push({ path: file, changes: result.changes }); + totalChanges += result.changes; + } + } + + console.log('\nMigration complete!\n'); + console.log(`Total changes: ${totalChanges}`); + console.log(`${dryRun ? 'Would modify' : 'Modified'} ${modifiedFiles.length} files:\n`); + + modifiedFiles + .sort((a, b) => b.changes - a.changes) + .forEach(({ path: filePath, changes }) => { + console.log(` - ${path.relative(process.cwd(), filePath)} (${changes} changes)`); + }); + + if (modifiedFiles.length === 0) { + console.log(' No files needed modification.'); + } +}; diff --git a/scripts/migrate-to-issue-log.ts b/scripts/migrate-to-issue-log.ts index bf82d85ca8..bd60a16c53 100755 --- a/scripts/migrate-to-issue-log.ts +++ b/scripts/migrate-to-issue-log.ts @@ -1,146 +1,10 @@ #!/usr/bin/env ts-node -import * as fs from 'fs'; -import * as path from 'path'; -import * as glob from 'glob'; +import { runLogMigration } from './log-migration-helper'; -interface Replacement { - pattern: RegExp; - replacement: string; -} - -const replacements: Replacement[] = [ - // Replace Log.log with IssueLog.log - { pattern: /\bLog\.log\(/g, replacement: 'IssueLog.log(' }, - // Replace Log.err with IssueLog.err - { pattern: /\bLog\.err\(/g, replacement: 'IssueLog.err(' }, - // Replace Log.info with IssueLog.info - { pattern: /\bLog\.info\(/g, replacement: 'IssueLog.info(' }, - // Replace Log.debug with IssueLog.debug - { pattern: /\bLog\.debug\(/g, replacement: 'IssueLog.debug(' }, - // Replace Log.verbose with IssueLog.verbose - { pattern: /\bLog\.verbose\(/g, replacement: 'IssueLog.verbose(' }, - // Replace Log.critical with IssueLog.critical - { pattern: /\bLog\.critical\(/g, replacement: 'IssueLog.critical(' }, -]; - -function updateImports(content: string): string { - // Check if file already imports IssueLog - const hasIssueLogImport = - /import\s*{[^}]*\bIssueLog\b[^}]*}\s*from\s*['"][^'"]*\/log['"]/.test(content); - - if (hasIssueLogImport) { - // If IssueLog is already imported, just remove Log from the import if it's not used elsewhere - return content; - } - - // Find existing Log import and add IssueLog to it - const logImportRegex = /import\s*{([^}]*\bLog\b[^}]*)}\s*from\s*(['"][^'"]*\/log['"])/; - const match = content.match(logImportRegex); - - if (match) { - const [fullMatch, imports, importPath] = match; - const importList = imports.split(',').map((s) => s.trim()); - - // Add IssueLog if not already there - if (!importList.includes('IssueLog')) { - importList.push('IssueLog'); - } - - // Check if Log is still used after replacements - let tempContent = content; - for (const { pattern, replacement } of replacements) { - tempContent = tempContent.replace(pattern, replacement); - } - - // Remove the import statement from check - tempContent = tempContent.replace(logImportRegex, ''); - - // If Log is no longer used, remove it from imports - const logStillUsed = /\bLog\b/.test(tempContent); - if (!logStillUsed) { - const logIndex = importList.indexOf('Log'); - if (logIndex > -1) { - importList.splice(logIndex, 1); - } - } - - const newImports = importList.join(', '); - const newImportStatement = `import { ${newImports} } from ${importPath}`; - content = content.replace(fullMatch, newImportStatement); - } - - return content; -} - -function processFile(filePath: string): { modified: boolean; changes: number } { - try { - let content = fs.readFileSync(filePath, 'utf8'); - const originalContent = content; - let changeCount = 0; - - // Count and apply replacements - for (const { pattern, replacement } of replacements) { - const matches = content.match(pattern); - if (matches) { - changeCount += matches.length; - content = content.replace(pattern, replacement); - } - } - - // Update imports if changes were made - if (changeCount > 0) { - content = updateImports(content); - } - - const modified = content !== originalContent; - - if (modified) { - fs.writeFileSync(filePath, content, 'utf8'); - } - - return { modified, changes: changeCount }; - } catch (error) { - console.error(`Error processing ${filePath}:`, error); - return { modified: false, changes: 0 }; - } -} - -function main() { - console.log('Migrating Log to IssueLog in features/issue directory...\n'); - - // Find all TypeScript files in features/issue directory - const files = glob.sync('src/app/features/issue/**/*.ts', { - ignore: ['**/*.spec.ts', '**/node_modules/**'], - absolute: true, - }); - - console.log(`Found ${files.length} TypeScript files in features/issue directory\n`); - - const modifiedFiles: { path: string; changes: number }[] = []; - let totalChanges = 0; - - for (const file of files) { - const result = processFile(file); - if (result.modified) { - modifiedFiles.push({ path: file, changes: result.changes }); - totalChanges += result.changes; - } - } - - console.log('\nMigration complete!\n'); - console.log(`Total changes: ${totalChanges}`); - console.log(`Modified ${modifiedFiles.length} files:\n`); - - modifiedFiles - .sort((a, b) => b.changes - a.changes) - .forEach(({ path: filePath, changes }) => { - console.log(` - ${path.relative(process.cwd(), filePath)} (${changes} changes)`); - }); - - if (modifiedFiles.length === 0) { - console.log(' No files needed modification.'); - } -} - -main(); +runLogMigration({ + description: 'Migrating Log to IssueLog in features/issue directory...', + fileLabel: 'TypeScript files in features/issue directory', + globPattern: 'src/app/features/issue/**/*.ts', + targetLogName: 'IssueLog', +}); diff --git a/scripts/migrate-to-plugin-log.ts b/scripts/migrate-to-plugin-log.ts index 3e41f7fed3..bd9e90bd9b 100755 --- a/scripts/migrate-to-plugin-log.ts +++ b/scripts/migrate-to-plugin-log.ts @@ -1,146 +1,10 @@ #!/usr/bin/env ts-node -import * as fs from 'fs'; -import * as path from 'path'; -import * as glob from 'glob'; +import { runLogMigration } from './log-migration-helper'; -interface Replacement { - pattern: RegExp; - replacement: string; -} - -const replacements: Replacement[] = [ - // Replace Log.log with PluginLog.log - { pattern: /\bLog\.log\(/g, replacement: 'PluginLog.log(' }, - // Replace Log.err with PluginLog.err - { pattern: /\bLog\.err\(/g, replacement: 'PluginLog.err(' }, - // Replace Log.info with PluginLog.info - { pattern: /\bLog\.info\(/g, replacement: 'PluginLog.info(' }, - // Replace Log.debug with PluginLog.debug - { pattern: /\bLog\.debug\(/g, replacement: 'PluginLog.debug(' }, - // Replace Log.verbose with PluginLog.verbose - { pattern: /\bLog\.verbose\(/g, replacement: 'PluginLog.verbose(' }, - // Replace Log.critical with PluginLog.critical - { pattern: /\bLog\.critical\(/g, replacement: 'PluginLog.critical(' }, -]; - -function updateImports(content: string): string { - // Check if file already imports PluginLog - const hasPluginLogImport = - /import\s*{[^}]*\bPluginLog\b[^}]*}\s*from\s*['"][^'"]*\/log['"]/.test(content); - - if (hasPluginLogImport) { - // If PluginLog is already imported, just remove Log from the import if it's not used elsewhere - return content; - } - - // Find existing Log import and add PluginLog to it - const logImportRegex = /import\s*{([^}]*\bLog\b[^}]*)}\s*from\s*(['"][^'"]*\/log['"])/; - const match = content.match(logImportRegex); - - if (match) { - const [fullMatch, imports, importPath] = match; - const importList = imports.split(',').map((s) => s.trim()); - - // Add PluginLog if not already there - if (!importList.includes('PluginLog')) { - importList.push('PluginLog'); - } - - // Check if Log is still used after replacements - let tempContent = content; - for (const { pattern, replacement } of replacements) { - tempContent = tempContent.replace(pattern, replacement); - } - - // Remove the import statement from check - tempContent = tempContent.replace(logImportRegex, ''); - - // If Log is no longer used, remove it from imports - const logStillUsed = /\bLog\b/.test(tempContent); - if (!logStillUsed) { - const logIndex = importList.indexOf('Log'); - if (logIndex > -1) { - importList.splice(logIndex, 1); - } - } - - const newImports = importList.join(', '); - const newImportStatement = `import { ${newImports} } from ${importPath}`; - content = content.replace(fullMatch, newImportStatement); - } - - return content; -} - -function processFile(filePath: string): { modified: boolean; changes: number } { - try { - let content = fs.readFileSync(filePath, 'utf8'); - const originalContent = content; - let changeCount = 0; - - // Count and apply replacements - for (const { pattern, replacement } of replacements) { - const matches = content.match(pattern); - if (matches) { - changeCount += matches.length; - content = content.replace(pattern, replacement); - } - } - - // Update imports if changes were made - if (changeCount > 0) { - content = updateImports(content); - } - - const modified = content !== originalContent; - - if (modified) { - fs.writeFileSync(filePath, content, 'utf8'); - } - - return { modified, changes: changeCount }; - } catch (error) { - console.error(`Error processing ${filePath}:`, error); - return { modified: false, changes: 0 }; - } -} - -function main() { - console.log('Migrating Log to PluginLog in plugins directory...\n'); - - // Find all TypeScript files in plugins directory - const files = glob.sync('src/app/plugins/**/*.ts', { - ignore: ['**/*.spec.ts', '**/node_modules/**'], - absolute: true, - }); - - console.log(`Found ${files.length} TypeScript files in plugins directory\n`); - - const modifiedFiles: { path: string; changes: number }[] = []; - let totalChanges = 0; - - for (const file of files) { - const result = processFile(file); - if (result.modified) { - modifiedFiles.push({ path: file, changes: result.changes }); - totalChanges += result.changes; - } - } - - console.log('\nMigration complete!\n'); - console.log(`Total changes: ${totalChanges}`); - console.log(`Modified ${modifiedFiles.length} files:\n`); - - modifiedFiles - .sort((a, b) => b.changes - a.changes) - .forEach(({ path: filePath, changes }) => { - console.log(` - ${path.relative(process.cwd(), filePath)} (${changes} changes)`); - }); - - if (modifiedFiles.length === 0) { - console.log(' No files needed modification.'); - } -} - -main(); +runLogMigration({ + description: 'Migrating Log to PluginLog in plugins directory...', + fileLabel: 'TypeScript files in plugins directory', + globPattern: 'src/app/plugins/**/*.ts', + targetLogName: 'PluginLog', +});