refakts
Version:
TypeScript refactoring tool built for AI coding agents to perform precise refactoring operations via command line instead of requiring complete code regeneration.
117 lines • 5.05 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExtractVariableCommand = void 0;
const ts_morph_1 = require("ts-morph");
const ast_service_1 = require("../services/ast-service");
const extraction_scope_analyzer_1 = require("../services/extraction-scope-analyzer");
const variable_name_validator_1 = require("../services/variable-name-validator");
const statement_inserter_1 = require("../services/statement-inserter");
class ExtractVariableCommand {
constructor() {
this.name = 'extract-variable';
this.description = 'Extract expression into a variable';
this.complete = true;
this.astService = new ast_service_1.ASTService();
this.scopeAnalyzer = new extraction_scope_analyzer_1.ExtractionScopeAnalyzer();
this.nameValidator = new variable_name_validator_1.VariableNameValidator();
this.statementInserter = new statement_inserter_1.StatementInserter();
}
async execute(file, options) {
this.validateOptions(options);
const sourceFile = this.astService.loadSourceFile(file);
const targetNode = this.findTargetNode(options);
await this.performExtraction(targetNode, options);
await this.astService.saveSourceFile(sourceFile);
}
findTargetNode(options) {
return this.astService.findNodeByLocation(options.location);
}
validateOptions(options) {
if (!options.location) {
throw new Error('Location format must be specified');
}
if (!options.name) {
throw new Error('--name must be specified');
}
}
getHelpText() {
return '\nExamples:\n refakts extract-variable "[src/file.ts 8:15-8:29]" --name "result"';
}
async performExtraction(targetNode, options) {
if (options.all) {
await this.extractAllOccurrences(targetNode, options.name);
}
else {
await this.extractSingleOccurrence(targetNode, options.name);
}
}
async extractSingleOccurrence(targetNode, variableName) {
this.validateExpressionNode(targetNode);
const scope = this.scopeAnalyzer.findExtractionScope(targetNode);
const uniqueName = this.nameValidator.generateUniqueName(variableName, scope);
this.statementInserter.insertVariableDeclaration(targetNode, uniqueName);
targetNode.replaceWithText(uniqueName);
}
async extractAllOccurrences(targetNode, variableName) {
this.validateExpressionNode(targetNode);
const allExpressions = this.findAllMatchingExpressions(targetNode);
this.validateExpressionsFound(allExpressions);
const groupedExpressions = this.groupExpressionsByScope(allExpressions);
this.extractInEachScope(groupedExpressions, variableName);
}
findAllMatchingExpressions(targetNode) {
const expressionText = targetNode.getText();
const sourceFile = targetNode.getSourceFile();
return this.findMatchingExpressions(sourceFile, expressionText);
}
validateExpressionNode(node) {
if (!ts_morph_1.Node.isExpression(node)) {
throw new Error('Selected node must be an expression');
}
}
validateExpressionsFound(expressions) {
if (expressions.length === 0) {
throw new Error('No matching expressions found');
}
}
groupExpressionsByScope(expressions) {
const groupedExpressions = new Map();
for (const expression of expressions) {
this.addExpressionToScope(groupedExpressions, expression);
}
return groupedExpressions;
}
addExpressionToScope(groupedExpressions, expression) {
const scope = this.scopeAnalyzer.findExtractionScope(expression);
if (!groupedExpressions.has(scope)) {
groupedExpressions.set(scope, []);
}
const expressionsForScope = groupedExpressions.get(scope);
if (expressionsForScope) {
expressionsForScope.push(expression);
}
}
extractInEachScope(expressionsByScope, variableName) {
for (const [scope, expressions] of expressionsByScope) {
const uniqueName = this.nameValidator.generateUniqueName(variableName, scope);
this.statementInserter.insertVariableDeclaration(expressions[0], uniqueName);
for (const expression of expressions) {
expression.replaceWithText(uniqueName);
}
}
}
findMatchingExpressions(scope, expressionText) {
const expressions = [];
scope.forEachDescendant((node) => {
this.addMatchingExpression(node, expressionText, expressions);
});
return expressions;
}
addMatchingExpression(node, expressionText, expressions) {
if (ts_morph_1.Node.isExpression(node) && node.getText() === expressionText) {
expressions.push(node);
}
}
}
exports.ExtractVariableCommand = ExtractVariableCommand;
//# sourceMappingURL=extract-variable-command.js.map