diff --git a/lib/core/schematics/migrations/7_0_0/index.ts b/lib/core/schematics/migrations/7_0_0/index.ts index a6c6d43de5..75f191934a 100644 --- a/lib/core/schematics/migrations/7_0_0/index.ts +++ b/lib/core/schematics/migrations/7_0_0/index.ts @@ -15,8 +15,8 @@ * limitations under the License. */ +import * as ts from 'typescript'; import { Rule, SchematicContext, SchematicsException, Tree } from '@angular-devkit/schematics'; -import { Project, NamedImports, SourceFile, ImportSpecifier, ImportDeclaration } from 'ts-morph'; interface MigrationData { change: { @@ -68,17 +68,13 @@ const migrations: MigrationData[] = [alfrescoApiServiceMigration, alfrescoApiMoc * @returns Schematic rule for updating imports */ export function updateAlfrescoApiImports(): Rule { - const project = new Project(); - - // eslint-disable-next-line @typescript-eslint/no-unused-vars return (tree: Tree, _context: SchematicContext) => { - tree.visit((filePath: string) => visitor(filePath, tree, project)); - + tree.visit((filePath: string) => visitor(filePath, tree)); return tree; }; } -export const visitor = (filePath: string, tree: Pick, project: Project) => { +export const visitor = (filePath: string, tree: Pick) => { if ( !filePath.includes('/.git/') && !filePath.includes('/node_modules/') && @@ -92,8 +88,11 @@ export const visitor = (filePath: string, tree: Pick throw new SchematicsException(`Could not read file: ${filePath}`); } + const fileContent = bufferFileContent.toString(); + const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true); + migrations.forEach((migrationData) => { - const fileWithUpdatedImport = moveImport(filePath, bufferFileContent, project, migrationData); + const fileWithUpdatedImport = moveImport(bufferFileContent, sourceFile, migrationData); if (fileWithUpdatedImport) { tree.overwrite(filePath, fileWithUpdatedImport); @@ -102,88 +101,70 @@ export const visitor = (filePath: string, tree: Pick } }; -const moveImport = (filePath: string, bufferFileContent: Buffer, project: Project, migrationData: MigrationData): string | undefined => { +export const moveImport = (bufferFileContent: Buffer, sourceFile: ts.SourceFile, migrationData: MigrationData): string | undefined => { const fileContent = bufferFileContent.toString(); const predictImport = fileContent.includes(migrationData.change.importedValue); if (predictImport) { - const sourceFile = project.getSourceFile(`migration-${filePath}`) ?? project.createSourceFile(`migration-${filePath}`, fileContent); + const moduleImports = sourceFile.statements.filter(ts.isImportDeclaration); - const alfrescoApiImportResult = getImportedValueFromSource(sourceFile, { - importedIdentifier: migrationData.change.importedValue, - from: migrationData.change.importSource + const importDeclaration = moduleImports.find((moduleImport) => { + const currentImportSource = moduleImport.moduleSpecifier.getText().replace(/['"]/g, ''); + return currentImportSource === migrationData.change.importSource; }); - if (alfrescoApiImportResult?.importedValue) { - if (alfrescoApiImportResult.allImportedValuesCount === 1) { - // There is only one import e.g. import { A } from 'A'; - // Therefore, we need to remove whole import statement - alfrescoApiImportResult.importSource?.remove(); + if (!importDeclaration || !importDeclaration.importClause?.namedBindings) { + return undefined; + } + + const namedBindings = importDeclaration.importClause.namedBindings; + if (!ts.isNamedImports(namedBindings)) { + return undefined; + } + + const namedImportsElements = namedBindings.elements; + const importedValue = namedImportsElements.find((binding) => binding.name.text === migrationData.change.importedValue); + + if (importedValue) { + let updatedContent = fileContent; + + if (namedImportsElements.length === 1) { + updatedContent = removeTextRange(updatedContent, importDeclaration.getFullStart(), importDeclaration.getEnd()); } else { - alfrescoApiImportResult.importedValue?.remove(); + updatedContent = removeTextRange(updatedContent, importedValue.getFullStart(), importedValue.getEnd()); } - const alfrescoContentServiceImport = getSourceImport(sourceFile, migrationData.to.importSource); - - if (alfrescoContentServiceImport) { - alfrescoContentServiceImport.addNamedImport(migrationData.to.importedValue); - } else { - sourceFile.insertStatements( - sourceFile.getImportDeclarations().length + 1, - `import { ${migrationData.to.importedValue} } from '${migrationData.to.importSource}';` + const alreadyImported = moduleImports.some((moduleImport) => { + const currentImportSource = moduleImport.moduleSpecifier.getText().replace(/['"]/g, ''); + return ( + currentImportSource === migrationData.to.importSource && + moduleImport.importClause?.namedBindings && + ts.isNamedImports(moduleImport.importClause.namedBindings) && + moduleImport.importClause.namedBindings.elements.some((el) => el.name.text === migrationData.to.importedValue) ); - } + }); - return sourceFile.getFullText(); + if (!alreadyImported) { + const firstNonImport = sourceFile.statements.find((stmt) => !ts.isImportDeclaration(stmt)); + const insertPosition = firstNonImport ? firstNonImport.getFullStart() : fileContent.length; + + updatedContent = + updatedContent.slice(0, insertPosition).replace(/\s*$/, '') + // usuĊ„ nadmiarowe linie + `\nimport { ${migrationData.to.importedValue} } from '${migrationData.to.importSource}';\n` + + updatedContent.slice(insertPosition); + } + updatedContent = updatedContent.replace(/^\s*\n+/g, ''); + updatedContent = updatedContent.replace(/\n{3,}/g, '\n\n'); + + return updatedContent; } } return undefined; }; -const getSourceImport = (sourceFile: SourceFile, from: string): ImportDeclaration | undefined => { - const moduleImports = sourceFile.getImportDeclarations(); - - const importDeclaration = moduleImports.find((moduleImport) => { - const currentImportSource = moduleImport.getModuleSpecifierValue(); - return currentImportSource === from; - }); - - return importDeclaration; -}; - -const getImportedValueFromSource = ( - sourceFile: SourceFile, - searchedImport: { - importedIdentifier: string; - from: string; - } -): { - importedValue: ImportSpecifier | undefined; - importSource: ImportDeclaration | undefined; - allImportedValuesCount: number | undefined; -} => { - const importSource = getSourceImport(sourceFile, searchedImport.from); - - if (!importSource) { - return { - importedValue: undefined, - importSource: undefined, - allImportedValuesCount: undefined - }; - } - - const importedValues = importSource?.getImportClause(); - const namedImports = importedValues?.getNamedBindings() as NamedImports; - const namedImportsElements = namedImports?.getElements() ?? []; - - const importedValue = namedImportsElements.find((binding) => binding.getName() === searchedImport.importedIdentifier); - - return { - importedValue, - importSource, - allImportedValuesCount: namedImportsElements.length - }; +export const removeTextRange = (text: string, start: number, end: number): string => { + return text.slice(0, start) + text.slice(end); }; export default updateAlfrescoApiImports;