- Add GitHub Actions workflow for automated CI/CD - Configure Node.js 18.x and 20.x matrix testing - Add TypeScript type checking step - Add ESLint code quality checks with enhanced rules - Add Prettier formatting verification - Add production build validation - Upload build artifacts for deployment - Set up automated testing on push/PR - Replace console.log with environment-aware logger - Add pre-commit hooks for code quality - Exclude archive folder from linting 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
184 lines
5.1 KiB
JavaScript
184 lines
5.1 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Console.log 교체 스크립트
|
|
* 프로젝트 전체의 console.log를 환경별 로거로 교체
|
|
*/
|
|
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const { glob } = require("glob");
|
|
|
|
// 교체할 패턴들
|
|
const replacements = [
|
|
{
|
|
pattern: /console\.log\(/g,
|
|
replacement: "syncLogger.info(",
|
|
description: "console.log → syncLogger.info",
|
|
},
|
|
{
|
|
pattern: /console\.error\(/g,
|
|
replacement: "syncLogger.error(",
|
|
description: "console.error → syncLogger.error",
|
|
},
|
|
{
|
|
pattern: /console\.warn\(/g,
|
|
replacement: "syncLogger.warn(",
|
|
description: "console.warn → syncLogger.warn",
|
|
},
|
|
{
|
|
pattern: /console\.info\(/g,
|
|
replacement: "syncLogger.info(",
|
|
description: "console.info → syncLogger.info",
|
|
},
|
|
];
|
|
|
|
// 파일별 특화된 로거 매핑
|
|
const fileLoggerMap = {
|
|
auth: "authLogger",
|
|
sync: "syncLogger",
|
|
network: "networkLogger",
|
|
storage: "storageLogger",
|
|
appwrite: "appwriteLogger",
|
|
};
|
|
|
|
// 파일 경로에 따른 적절한 로거 결정
|
|
function getLoggerForFile(filePath) {
|
|
const lowerPath = filePath.toLowerCase();
|
|
|
|
for (const [keyword, logger] of Object.entries(fileLoggerMap)) {
|
|
if (lowerPath.includes(keyword)) {
|
|
return logger;
|
|
}
|
|
}
|
|
|
|
// 기본값은 일반 logger
|
|
return "logger";
|
|
}
|
|
|
|
// 파일 처리 함수
|
|
async function processFile(filePath) {
|
|
try {
|
|
const content = fs.readFileSync(filePath, "utf8");
|
|
const originalContent = content;
|
|
|
|
// 이미 logger import가 있는지 확인
|
|
const hasLoggerImport = content.includes("from '@/utils/logger'");
|
|
const appropriateLogger = getLoggerForFile(filePath);
|
|
|
|
let newContent = content;
|
|
let hasChanges = false;
|
|
|
|
// console 사용이 있는지 확인
|
|
const hasConsoleUsage = /console\.(log|error|warn|info)\(/.test(content);
|
|
|
|
if (hasConsoleUsage) {
|
|
// console.log 등을 적절한 로거로 교체
|
|
replacements.forEach(({ pattern, description }) => {
|
|
const loggerMethod = pattern.source.includes("error")
|
|
? "error"
|
|
: pattern.source.includes("warn")
|
|
? "warn"
|
|
: "info";
|
|
|
|
const replacement = `${appropriateLogger}.${loggerMethod}(`;
|
|
|
|
if (pattern.test(newContent)) {
|
|
newContent = newContent.replace(pattern, replacement);
|
|
hasChanges = true;
|
|
console.log(
|
|
` - ${description} (using ${appropriateLogger}.${loggerMethod})`
|
|
);
|
|
}
|
|
});
|
|
|
|
// logger import 추가 (필요한 경우)
|
|
if (hasChanges && !hasLoggerImport) {
|
|
// import 구문 찾기
|
|
const importMatch = newContent.match(
|
|
/^(import[\s\S]*?from ['"][^'"]+['"];?\s*\n)/m
|
|
);
|
|
|
|
if (importMatch) {
|
|
const lastImportEnd = importMatch.index + importMatch[1].length;
|
|
const beforeImports = newContent.slice(0, lastImportEnd);
|
|
const afterImports = newContent.slice(lastImportEnd);
|
|
|
|
let importStatement;
|
|
if (appropriateLogger === "logger") {
|
|
importStatement = "import { logger } from '@/utils/logger';\n";
|
|
} else {
|
|
importStatement = `import { ${appropriateLogger} } from '@/utils/logger';\n`;
|
|
}
|
|
|
|
newContent = beforeImports + importStatement + afterImports;
|
|
console.log(` - Added import: ${importStatement.trim()}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 변경사항이 있으면 파일 저장
|
|
if (hasChanges && newContent !== originalContent) {
|
|
fs.writeFileSync(filePath, newContent, "utf8");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
} catch (error) {
|
|
console.error(`Error processing ${filePath}:`, error.message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 메인 실행 함수
|
|
async function main() {
|
|
console.log("🔄 Console.log → Logger 교체 작업 시작\n");
|
|
|
|
// src 폴더 내의 TypeScript/JavaScript 파일들 찾기
|
|
const files = await glob("src/**/*.{ts,tsx,js,jsx}", {
|
|
ignore: [
|
|
"src/utils/logger.ts", // 로거 파일 자체는 제외
|
|
"src/**/*.test.{ts,tsx,js,jsx}", // 테스트 파일 제외
|
|
"src/**/*.spec.{ts,tsx,js,jsx}", // 스펙 파일 제외
|
|
],
|
|
cwd: process.cwd(),
|
|
});
|
|
|
|
console.log(`📁 총 ${files.length}개 파일 검사 중...\n`);
|
|
|
|
let processedCount = 0;
|
|
let changedCount = 0;
|
|
|
|
for (const file of files) {
|
|
const filePath = path.resolve(file);
|
|
console.log(`🔍 처리 중: ${file}`);
|
|
|
|
const hasChanges = await processFile(filePath);
|
|
processedCount++;
|
|
|
|
if (hasChanges) {
|
|
changedCount++;
|
|
console.log(` ✅ 변경 완료`);
|
|
} else {
|
|
console.log(` ⏭️ 변경 없음`);
|
|
}
|
|
|
|
console.log("");
|
|
}
|
|
|
|
console.log("🎉 Console.log → Logger 교체 작업 완료");
|
|
console.log(`📊 결과: ${changedCount}/${processedCount} 파일 변경됨\n`);
|
|
|
|
if (changedCount > 0) {
|
|
console.log("🔧 다음 단계:");
|
|
console.log("1. npm run build로 빌드 에러 확인");
|
|
console.log("2. 변경된 파일들 검토");
|
|
console.log("3. 개발 서버에서 로그 출력 테스트");
|
|
}
|
|
}
|
|
|
|
// 스크립트 실행
|
|
if (require.main === module) {
|
|
main().catch(console.error);
|
|
}
|