ntts
Version:
A CLI tool for refactoring an existing NodeJs application to a fully functional TypeScript application.
94 lines (85 loc) • 4.05 kB
text/typescript
import {
ImportDeclaration, ImportSpecifier, SourceFile, VariableDeclarationKind,
} from 'ts-morph';
import ImportCreator from '../../helpers/import-creator/import-creator';
import VariableNameGenerator from '../../helpers/variable-name-generator/variable-name-generator';
class ImportClauseRefactor {
static refactorImportClause = (
importStatement: ImportDeclaration,
usedImportNames: string[],
sourceFile: SourceFile,
): string[] => {
const moduleSpecifier = importStatement.getModuleSpecifierValue();
const importedSourceFile = importStatement.isModuleSpecifierRelative() && importStatement.getModuleSpecifierSourceFile();
if (importedSourceFile) {
this.refactorDefaultImport(importStatement, moduleSpecifier, importedSourceFile, sourceFile);
return this.refactorNamedImports(importStatement, usedImportNames, importedSourceFile, sourceFile);
}
return usedImportNames;
};
private static refactorDefaultImport = (
importStatement: ImportDeclaration,
moduleSpecifier: string,
importedSourceFile: SourceFile,
sourceFile: SourceFile,
) => {
const defaultImport = importStatement.getDefaultImport()?.getText();
if (defaultImport && !this.hasDefaultExport(importedSourceFile)) {
if (importStatement.getNamedImports().length > 0) {
ImportCreator.addNamespaceImport(defaultImport, moduleSpecifier, sourceFile);
importStatement.removeDefaultImport();
} else {
importStatement.removeDefaultImport().setNamespaceImport(defaultImport);
}
}
};
private static refactorNamedImports = (
importStatement: ImportDeclaration,
usedImportNames: string[],
importedSourceFile: SourceFile,
sourceFile: SourceFile,
): string[] => {
const namedImports = importStatement.getNamedImports();
if (namedImports.length > 0) {
const exportedList = this.getExportedList(importedSourceFile);
const defaultImports = namedImports.filter((namedImport) => !exportedList.includes(namedImport.getName()));
const defaultImportName = importStatement.getDefaultImport()?.getText();
if (defaultImportName && defaultImports.length > 0) {
this.createObjectDestructuring(importStatement, defaultImports, defaultImportName, sourceFile);
defaultImports.forEach((d) => d.remove());
}
if (!defaultImportName && defaultImports.length > 0) {
const importName = VariableNameGenerator.variableNameFromImportId(importStatement.getModuleSpecifierValue());
const initializer = VariableNameGenerator.getUsableVariableName(importName, usedImportNames);
importStatement.setDefaultImport(initializer);
this.createObjectDestructuring(importStatement, defaultImports, initializer, sourceFile);
defaultImports.forEach((d) => d.remove());
return usedImportNames.concat(importName);
}
}
return usedImportNames;
};
private static getExportedList = (importedSourceFile: SourceFile): string[] => importedSourceFile
.getExportDeclarations()
.reduce((exportedNames: string[], exportDeclaration) => {
const names = exportDeclaration
.getNamedExports()
.map((named) => named.getAliasNode()?.getText() || named.getName());
return exportedNames.concat(...names);
}, []);
private static createObjectDestructuring = (
importStatement: ImportDeclaration,
defaultImports: ImportSpecifier[],
initializer: string,
sourceFile: SourceFile,
) => {
const index = importStatement.getChildIndex();
const objectDestructuring = defaultImports.map((i) => (i.getAliasNode() ? `${i.getName()}: ${i.getAliasNode()}` : i.getName())).join(', ');
sourceFile.insertVariableStatement(index + 1, {
declarationKind: VariableDeclarationKind.Const,
declarations: [{ name: `{ ${objectDestructuring} }`, initializer }],
});
};
private static hasDefaultExport = (sourceFile: SourceFile): boolean => !!sourceFile.getDefaultExportSymbol();
}
export default ImportClauseRefactor;