diff --git a/projects/element-ng/schematics/migrations/action-modal-migration/action-modal-migration.spec.ts b/projects/element-ng/schematics/migrations/action-modal-migration/action-modal-migration.spec.ts index 345179942..5246d2833 100644 --- a/projects/element-ng/schematics/migrations/action-modal-migration/action-modal-migration.spec.ts +++ b/projects/element-ng/schematics/migrations/action-modal-migration/action-modal-migration.spec.ts @@ -243,7 +243,28 @@ describe('action modal migration', () => { const expected = [ `import { Component, inject } from '@angular/core';`, - `import { AlertDialogResult, SiActionDialogService } from '@siemens/element-ng/action-modal';`, + `import { SiActionDialogService } from '@siemens/element-ng/action-modal';`, + `@Component({ selector: 'app-test-action-modal' })`, + `export class TestActionModalComponent {`, + `showDialog() { inject(SiActionDialogService).showActionDialog({ type: 'alert', message: 'Message', heading: 'Heading', confirmBtnName: 'Confirmation button text' }) } }` + ]; + + await checkTemplateMigration(original, expected); + }); + + it('should clean-up imports', async () => { + const original = [ + `import { Component, inject } from '@angular/core';`, + `import { AlertDialogResult, SiActionDialogService, SiUnrelated } from '@siemens/element-ng';`, + `import { DeleteConfirmationDialogResult } from '@siemens/element-ng/action-modal';`, + `@Component({ selector: 'app-test-action-modal' })`, + `export class TestActionModalComponent {`, + `showDialog() { inject(SiActionDialogService).showAlertDialog('Message', 'Heading', 'Confirmation button text') } }` + ]; + + const expected = [ + `import { Component, inject } from '@angular/core';`, + `import { SiActionDialogService, SiUnrelated } from '@siemens/element-ng';`, `@Component({ selector: 'app-test-action-modal' })`, `export class TestActionModalComponent {`, `showDialog() { inject(SiActionDialogService).showActionDialog({ type: 'alert', message: 'Message', heading: 'Heading', confirmBtnName: 'Confirmation button text' }) } }` diff --git a/projects/element-ng/schematics/migrations/action-modal-migration/action-modal-migration.ts b/projects/element-ng/schematics/migrations/action-modal-migration/action-modal-migration.ts index 8997e1db5..7fcc8d63e 100644 --- a/projects/element-ng/schematics/migrations/action-modal-migration/action-modal-migration.ts +++ b/projects/element-ng/schematics/migrations/action-modal-migration/action-modal-migration.ts @@ -55,8 +55,13 @@ export const actionModalMigrationRule = (options: { path: string }): Rule => { } const pendingTransformations: CodeTransformation[] = []; + const usedIdentifier = new Set(); const visitNodeAndCollectTransformations = (node: ts.Node): void => { + if (ts.isImportDeclaration(node)) { + return; + } + // Collect method call transformations if (ts.isCallExpression(node)) { const methodTransformation = createActionDialogMethodCallTransformation(node); @@ -70,12 +75,19 @@ export const actionModalMigrationRule = (options: { path: string }): Rule => { const typeTransformation = createActionDialogTypeTransformation(node); if (typeTransformation) { pendingTransformations.push(typeTransformation); + // We remove the usage here. In order to avoid counting it as used identifier, we skip further processing. + return; } } + if (ts.isIdentifier(node)) { + usedIdentifier.add(node.text); + } + node.forEachChild(visitNodeAndCollectTransformations); }; sourceFile.forEachChild(visitNodeAndCollectTransformations); + pendingTransformations.push(...removeUnusedImports(sourceFile, usedIdentifier)); if (pendingTransformations.length > 0) { applyCodeTransformations(tree, filePath, pendingTransformations); @@ -117,8 +129,7 @@ const createActionDialogMethodCallTransformation = ( return { node, - newCode, - type: 'method-call' + newCode }; }; @@ -137,8 +148,7 @@ const createActionDialogTypeTransformation = ( return { node, - newCode: `'${matchingReplacement.new}'`, - type: 'type-reference' + newCode: `'${matchingReplacement.new}'` }; }; @@ -192,3 +202,57 @@ const applyCodeTransformations = ( }); tree.commitUpdate(recorder); }; + +const removeUnusedImports = ( + sourceFile: ts.SourceFile, + usedIdentifier: Set +): CodeTransformation[] => { + const printer = ts.createPrinter(); + const importFinder = (node: ts.Node): CodeTransformation | undefined => { + if ( + ts.isImportDeclaration(node) && + ts.isStringLiteral(node.moduleSpecifier) && + /@(siemens|simpl)\/element-ng(\/action-modal)?/.test(node.moduleSpecifier.text) && + node.importClause?.namedBindings && + ts.isNamedImports(node.importClause.namedBindings) + ) { + const usedBindings = node.importClause.namedBindings.elements.filter( + element => + // This script anyway is not capable of handling aliasing imports, so we can ignore them here. + usedIdentifier.has(element.name.text) || !ACTION_MODAL_SYMBOLS.includes(element.name.text) + ); + + if (usedBindings.length === node.importClause.namedBindings.elements.length) { + // All bindings are used, no changes needed + return undefined; + } + + if (usedBindings.length === 0) { + // No bindings are used, remove the entire import statement + return { + node, + newCode: '' + }; + } + + // Recreate import statement with only used bindings + const newImport = ts.factory.createImportDeclaration( + node.modifiers, + ts.factory.createImportClause( + node.importClause.isTypeOnly, + node.importClause.name, + ts.factory.createNamedImports(usedBindings) + ), + node.moduleSpecifier, + node.attributes + ); + + return { + node, + newCode: printer.printNode(ts.EmitHint.Unspecified, newImport, sourceFile) + }; + } + return undefined; + }; + return sourceFile.statements.map(importFinder).filter(transform => transform !== undefined); +}; diff --git a/projects/element-ng/schematics/migrations/action-modal-migration/action-modal.mappings.ts b/projects/element-ng/schematics/migrations/action-modal-migration/action-modal.mappings.ts index 27dbe8663..f81ecfa91 100644 --- a/projects/element-ng/schematics/migrations/action-modal-migration/action-modal.mappings.ts +++ b/projects/element-ng/schematics/migrations/action-modal-migration/action-modal.mappings.ts @@ -30,7 +30,6 @@ export interface DialogMethodConfig { export interface CodeTransformation { node: ts.Node; newCode: string; - type: 'method-call' | 'type-reference'; } export const DIALOG_METHOD_CONFIGS: Record = {