-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit f1bba86
Showing
3 changed files
with
556 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
import * as fs from 'fs'; | ||
import * as path from 'path'; | ||
import * as ts from 'typescript'; | ||
|
||
const REMOVABLE_FUNCTIONS = [ | ||
'countEvent', | ||
'countRareEvent', | ||
'scopedProfileSectionStart', | ||
'scopedProfileSectionEnd', | ||
'profileSectionStart', | ||
'profileSectionEnd', | ||
'playbackLogNote', | ||
'debug', | ||
'info' | ||
]; | ||
|
||
function shouldRemoveCall(node) { | ||
if (!ts.isCallExpression(node)) return false; | ||
const expression = node.expression; | ||
|
||
if (ts.isPropertyAccessExpression(expression)) { | ||
const methodName = expression.name.getText(); | ||
if (expression.expression.getText() === 'console' && methodName === 'log') { | ||
return true; | ||
} | ||
if (REMOVABLE_FUNCTIONS.includes(methodName)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
function processNode(context) { | ||
function visit(node) { | ||
if (!node) return node; | ||
|
||
if (ts.isIfStatement(node)) { | ||
let thenStatement = ts.visitNode(node.thenStatement, visit); | ||
let elseStatement = node.elseStatement ? ts.visitNode(node.elseStatement, visit) : undefined; | ||
|
||
// If then branch is just a removable call | ||
if (ts.isExpressionStatement(node.thenStatement) && | ||
shouldRemoveCall(node.thenStatement.expression)) { | ||
return elseStatement; | ||
} | ||
|
||
// If else branch is just a removable call | ||
if (elseStatement && ts.isExpressionStatement(node.elseStatement) && | ||
shouldRemoveCall(node.elseStatement.expression)) { | ||
elseStatement = undefined; | ||
} | ||
|
||
if (!thenStatement) { | ||
return elseStatement; | ||
} | ||
|
||
if (thenStatement !== node.thenStatement || elseStatement !== node.elseStatement) { | ||
return ts.factory.updateIfStatement( | ||
node, | ||
node.expression, | ||
thenStatement, | ||
elseStatement | ||
); | ||
} | ||
return node; | ||
} | ||
|
||
if (ts.isExpressionStatement(node) && shouldRemoveCall(node.expression)) { | ||
return undefined; | ||
} | ||
|
||
return ts.visitEachChild(node, visit, context); | ||
} | ||
|
||
return visit; | ||
} | ||
|
||
function removeCommentsAndEmptyLines(sourceFile) { | ||
// Create a custom transformer to preserve eslint comments | ||
const transformer = (context) => (rootNode) => { | ||
function visitNode(node) { | ||
// Save eslint comments | ||
const leadingComments = ts.getLeadingCommentRanges(sourceFile.text, node.pos); | ||
const trailingComments = ts.getTrailingCommentRanges(sourceFile.text, node.end); | ||
|
||
const eslintComments = []; | ||
if (leadingComments) { | ||
leadingComments.forEach(comment => { | ||
const commentText = sourceFile.text.slice(comment.pos, comment.end); | ||
if (commentText.includes('eslint')) { | ||
eslintComments.push(commentText); | ||
} | ||
}); | ||
} | ||
|
||
if (trailingComments) { | ||
trailingComments.forEach(comment => { | ||
const commentText = sourceFile.text.slice(comment.pos, comment.end); | ||
if (commentText.includes('eslint')) { | ||
eslintComments.push(commentText); | ||
} | ||
}); | ||
} | ||
|
||
return ts.visitEachChild(node, visitNode, context); | ||
} | ||
|
||
return ts.visitNode(rootNode, visitNode); | ||
}; | ||
|
||
// Transform to preserve eslint comments | ||
const result = ts.transform(sourceFile, [transformer]); | ||
|
||
// Print with all comments initially | ||
const printer = ts.createPrinter({ | ||
removeComments: false, | ||
newLine: ts.NewLineKind.LineFeed | ||
}); | ||
|
||
const printed = printer.printFile(result.transformed[0]); | ||
|
||
// Filter empty lines but preserve eslint comments | ||
return printed | ||
.split('\n') | ||
.filter(line => line.includes('eslint') || line.trim().length > 0) | ||
.join('\n'); | ||
} | ||
|
||
function processFile(filePath) { | ||
try { | ||
const sourceText = fs.readFileSync(filePath, 'utf-8'); | ||
const sourceFile = ts.createSourceFile( | ||
filePath, | ||
sourceText, | ||
ts.ScriptTarget.Latest, | ||
true | ||
); | ||
|
||
const result = ts.transform(sourceFile, [ | ||
(context) => (node) => ts.visitNode(node, processNode(context)) | ||
]); | ||
|
||
const transformedCode = removeCommentsAndEmptyLines(result.transformed[0]); | ||
fs.writeFileSync(filePath, transformedCode); | ||
console.log(`Processed: ${filePath}`); | ||
} catch (error) { | ||
console.error(`Error processing ${filePath}:`, error); | ||
} | ||
} | ||
|
||
function processDirectory(directoryPath) { | ||
const files = fs.readdirSync(directoryPath); | ||
|
||
files.forEach(file => { | ||
const filePath = path.join(directoryPath, file); | ||
const stats = fs.statSync(filePath); | ||
|
||
if (stats.isDirectory()) { | ||
processDirectory(filePath); | ||
} else if (file.endsWith('.ts') || file.endsWith('.tsx')) { | ||
processFile(filePath); | ||
} | ||
}); | ||
} | ||
|
||
const projectDirectory = process.argv[2] || '.'; | ||
if (!fs.existsSync(projectDirectory)) { | ||
console.error('Directory does not exist:', projectDirectory); | ||
process.exit(1); | ||
} | ||
|
||
processDirectory(projectDirectory); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
#!/usr/bin/env node | ||
|
||
const ts = require('typescript'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
|
||
// AST Transformer | ||
function createTransformer() { | ||
return (context) => { | ||
const visit = (node) => { | ||
if (ts.isIfStatement(node)) { | ||
const thenStatement = node.thenStatement; | ||
if (ts.isBlock(thenStatement) && thenStatement.statements.length === 1) { | ||
const singleStatement = thenStatement.statements[0]; | ||
return ts.factory.updateIfStatement( | ||
node, | ||
node.expression, | ||
singleStatement, | ||
node.elseStatement ? visit(node.elseStatement) : undefined | ||
); | ||
} | ||
} | ||
|
||
if (ts.isBlock(node) && node.parent && ts.isIfStatement(node.parent) && node.parent.elseStatement === node) { | ||
if (node.statements.length === 1) { | ||
return node.statements[0]; | ||
} | ||
} | ||
|
||
return ts.visitEachChild(node, visit, context); | ||
}; | ||
|
||
return (sourceFile) => ts.visitNode(sourceFile, visit); | ||
}; | ||
} | ||
|
||
// Code transformation function | ||
function transformCode(sourceCode) { | ||
const sourceFile = ts.createSourceFile( | ||
'temp.ts', | ||
sourceCode, | ||
ts.ScriptTarget.Latest, | ||
true | ||
); | ||
|
||
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); | ||
const result = ts.transform(sourceFile, [createTransformer()]); | ||
const transformedSourceFile = result.transformed[0]; | ||
|
||
return printer.printFile(transformedSourceFile); | ||
} | ||
|
||
// File processing | ||
function processFile(filePath, dryRun) { | ||
try { | ||
const sourceCode = fs.readFileSync(filePath, 'utf-8'); | ||
const transformed = transformCode(sourceCode); | ||
|
||
if (sourceCode !== transformed) { | ||
if (dryRun) { | ||
console.log(`\nWould transform ${filePath}:`); | ||
console.log('----------------------------------------'); | ||
console.log(transformed); | ||
console.log('----------------------------------------'); | ||
} else { | ||
fs.writeFileSync(filePath, transformed); | ||
console.log(`✓ Transformed: ${filePath}`); | ||
} | ||
} else { | ||
console.log(`- Skipped (no changes needed): ${filePath}`); | ||
} | ||
} catch (error) { | ||
console.error(`Error processing ${filePath}:`, error); | ||
} | ||
} | ||
|
||
// Directory scanning | ||
function getAllTypeScriptFiles(dir) { | ||
let results = []; | ||
|
||
const items = fs.readdirSync(dir); | ||
|
||
for (const item of items) { | ||
const fullPath = path.join(dir, item); | ||
const stat = fs.statSync(fullPath); | ||
|
||
if (stat.isDirectory()) { | ||
if (item === 'node_modules' || item === '.git' || item === 'dist' || item === 'build') { | ||
continue; | ||
} | ||
results = results.concat(getAllTypeScriptFiles(fullPath)); | ||
} else if (item.endsWith('.ts') || item.endsWith('.tsx')) { | ||
results.push(fullPath); | ||
} | ||
} | ||
|
||
return results; | ||
} | ||
|
||
// Main processing function | ||
function processDirectory(dirPath, dryRun) { | ||
console.log(`\nProcessing directory: ${dirPath}`); | ||
console.log(`Mode: ${dryRun ? 'Dry Run (no changes will be made)' : 'Live Run'}`); | ||
|
||
try { | ||
const files = getAllTypeScriptFiles(dirPath); | ||
|
||
if (files.length === 0) { | ||
console.log('No TypeScript files found'); | ||
return; | ||
} | ||
|
||
console.log(`Found ${files.length} TypeScript files\n`); | ||
|
||
for (const file of files) { | ||
processFile(file, dryRun); | ||
} | ||
|
||
console.log('\n----------------------------------------'); | ||
console.log(`Completed processing ${files.length} files`); | ||
if (dryRun) { | ||
console.log('This was a dry run - no files were modified'); | ||
} | ||
} catch (error) { | ||
console.error('Error processing directory:', error); | ||
process.exit(1); | ||
} | ||
} | ||
|
||
// CLI argument parsing | ||
function parseArgs() { | ||
const args = process.argv.slice(2); | ||
const dryRun = args.includes('--dry-run'); | ||
const dirIndex = args.findIndex(arg => !arg.startsWith('--')); | ||
const dirPath = dirIndex >= 0 ? args[dirIndex] : '.'; | ||
|
||
return { dirPath, dryRun }; | ||
} | ||
|
||
// Help text | ||
function printHelp() { | ||
console.log(` | ||
TypeScript Conditional Statement Transformer | ||
Usage: | ||
node transform.js [options] [directory] | ||
Options: | ||
--dry-run Show what changes would be made without actually making them | ||
--help Show this help message | ||
Arguments: | ||
directory Directory to process (default: current directory) | ||
Examples: | ||
node transform.js # Process current directory | ||
node transform.js src # Process src directory | ||
node transform.js --dry-run src # Dry run on src directory | ||
`); | ||
} | ||
|
||
// Main execution | ||
if (require.main === module) { | ||
const args = process.argv.slice(2); | ||
|
||
if (args.includes('--help')) { | ||
printHelp(); | ||
process.exit(0); | ||
} | ||
|
||
const { dirPath, dryRun } = parseArgs(); | ||
|
||
if (!fs.existsSync(dirPath)) { | ||
console.error(`Error: Directory '${dirPath}' does not exist`); | ||
process.exit(1); | ||
} | ||
|
||
processDirectory(dirPath, dryRun); | ||
} |
Oops, something went wrong.