@siemens/element-ng 48.3.0 → 48.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/accordion/index.d.ts +0 -1
  2. package/chat-messages/index.d.ts +176 -156
  3. package/dashboard/index.d.ts +2 -3
  4. package/fesm2022/siemens-element-ng-accordion.mjs +2 -3
  5. package/fesm2022/siemens-element-ng-accordion.mjs.map +1 -1
  6. package/fesm2022/siemens-element-ng-chat-messages.mjs +291 -168
  7. package/fesm2022/siemens-element-ng-chat-messages.mjs.map +1 -1
  8. package/fesm2022/siemens-element-ng-dashboard.mjs +7 -11
  9. package/fesm2022/siemens-element-ng-dashboard.mjs.map +1 -1
  10. package/fesm2022/siemens-element-ng-datepicker.mjs +5 -3
  11. package/fesm2022/siemens-element-ng-datepicker.mjs.map +1 -1
  12. package/fesm2022/siemens-element-ng-filtered-search.mjs +4 -8
  13. package/fesm2022/siemens-element-ng-filtered-search.mjs.map +1 -1
  14. package/fesm2022/siemens-element-ng-loading-spinner.mjs +2 -2
  15. package/fesm2022/siemens-element-ng-loading-spinner.mjs.map +1 -1
  16. package/fesm2022/siemens-element-ng-navbar-vertical.mjs +21 -24
  17. package/fesm2022/siemens-element-ng-navbar-vertical.mjs.map +1 -1
  18. package/fesm2022/siemens-element-ng-select.mjs +4 -0
  19. package/fesm2022/siemens-element-ng-select.mjs.map +1 -1
  20. package/fesm2022/siemens-element-ng-side-panel.mjs +3 -5
  21. package/fesm2022/siemens-element-ng-side-panel.mjs.map +1 -1
  22. package/fesm2022/siemens-element-ng-status-bar.mjs +3 -4
  23. package/fesm2022/siemens-element-ng-status-bar.mjs.map +1 -1
  24. package/fesm2022/siemens-element-ng-tour.mjs +58 -21
  25. package/fesm2022/siemens-element-ng-tour.mjs.map +1 -1
  26. package/fesm2022/siemens-element-ng-translate.mjs.map +1 -1
  27. package/fesm2022/siemens-element-ng-tree-view.mjs +4 -4
  28. package/fesm2022/siemens-element-ng-tree-view.mjs.map +1 -1
  29. package/filtered-search/index.d.ts +6 -7
  30. package/navbar-vertical/index.d.ts +3 -4
  31. package/package.json +15 -15
  32. package/schematics/migrations/action-modal-migration/action-modal-migration.js +45 -4
  33. package/schematics/migrations/data/output-names.js +0 -1
  34. package/schematics/migrations/data/symbol-removals.js +0 -9
  35. package/schematics/migrations/element-migration/element-migration.js +1 -9
  36. package/schematics/migrations/wizard-migration/index.js +2 -10
  37. package/schematics/ts-import-to-siemens-migration/index.js +130 -25
  38. package/schematics/ts-import-to-siemens-migration/mappings/charts-ng-mappings.js +2 -1
  39. package/schematics/ts-import-to-siemens-migration/mappings/dashboards-ng-mappings.js +3 -1
  40. package/schematics/ts-import-to-siemens-migration/mappings/element-ng-mappings.js +54 -0
  41. package/schematics/ts-import-to-siemens-migration/mappings/element-translate-ng-mappings.js +1 -1
  42. package/schematics/ts-import-to-siemens-migration/mappings/index.js +1 -0
  43. package/schematics/ts-import-to-siemens-migration/mappings/maps-ng-mappings.js +2 -1
  44. package/schematics/ts-import-to-siemens-migration/mappings/native-charts-ng-mappings.js +33 -0
  45. package/schematics/utils/project-utils.js +1 -1
  46. package/schematics/utils/schematics-file-system.js +1 -1
  47. package/schematics/utils/template-utils.js +5 -5
  48. package/schematics/utils/ts-utils.js +1 -1
  49. package/select/index.d.ts +5 -0
  50. package/status-bar/index.d.ts +0 -1
  51. package/template-i18n.json +1 -0
  52. package/tour/index.d.ts +4 -2
  53. package/translate/index.d.ts +1 -0
@@ -1,5 +1,5 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { OnInit, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
2
+ import { OnInit, OnChanges, SimpleChanges } from '@angular/core';
3
3
  import { BackgroundColorVariant } from '@siemens/element-ng/common';
4
4
  import { TypeaheadOption } from '@siemens/element-ng/typeahead';
5
5
  import { TranslatableString } from '@siemens/element-translate-ng/translate';
@@ -150,7 +150,7 @@ interface InternalCriterionDefinition extends CriterionDefinition {
150
150
  translatedLabel: string;
151
151
  }
152
152
 
153
- declare class SiFilteredSearchComponent implements OnInit, OnChanges, OnDestroy {
153
+ declare class SiFilteredSearchComponent implements OnInit, OnChanges {
154
154
  private static readonly criterionRegex;
155
155
  /**
156
156
  * Output callback event that provides an object describing the
@@ -370,10 +370,10 @@ declare class SiFilteredSearchComponent implements OnInit, OnChanges, OnDestroy
370
370
  private typeaheadInputChange;
371
371
  /** Used to debounce the Search emissions */
372
372
  private searchEmitQueue;
373
- private destroySubscriptions;
374
- private cdRef;
375
- private translateService;
376
- private locale;
373
+ private readonly destroyRef;
374
+ private readonly cdRef;
375
+ private readonly translateService;
376
+ private readonly locale;
377
377
  /**
378
378
  * The cache is used to control when the interceptDisplayedCriteria event needs to be called.
379
379
  * Every time a criteria gain the focus we have to reset the cache to call the interceptor.
@@ -388,7 +388,6 @@ declare class SiFilteredSearchComponent implements OnInit, OnChanges, OnDestroy
388
388
  constructor();
389
389
  ngOnChanges(changes: SimpleChanges): void;
390
390
  ngOnInit(): void;
391
- ngOnDestroy(): void;
392
391
  private initCriteria;
393
392
  private initValue;
394
393
  /**
@@ -1,6 +1,6 @@
1
1
  import * as _siemens_element_translate_ng_translate_types from '@siemens/element-translate-ng/translate-types';
2
2
  import * as _angular_core from '@angular/core';
3
- import { OnChanges, OnInit, OnDestroy, SimpleChanges } from '@angular/core';
3
+ import { OnChanges, OnInit, SimpleChanges } from '@angular/core';
4
4
  import { NavigationExtras, IsActiveMatchOptions, ActivatedRoute } from '@angular/router';
5
5
  import { MenuItem } from '@siemens/element-ng/common';
6
6
  import { TranslatableString } from '@siemens/element-translate-ng/translate';
@@ -91,7 +91,8 @@ declare class SiNavbarVerticalItemGuardDirective {
91
91
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SiNavbarVerticalItemGuardDirective, never>;
92
92
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SiNavbarVerticalItemGuardDirective, "[siNavbarVerticalItemGuard]", never, {}, {}, never, never, true, never>;
93
93
  }
94
- declare class SiNavbarVerticalComponent implements OnChanges, OnInit, OnDestroy {
94
+ declare class SiNavbarVerticalComponent implements OnChanges, OnInit {
95
+ protected readonly icons: Record<"elementSearch" | "elementDoubleLeft" | "elementDoubleRight", string>;
95
96
  /**
96
97
  * Whether the navbar-vertical is collapsed.
97
98
  *
@@ -197,12 +198,10 @@ declare class SiNavbarVerticalComponent implements OnChanges, OnInit, OnDestroy
197
198
  private readonly itemsToComponents;
198
199
  protected readonly smallScreen: _angular_core.WritableSignal<boolean>;
199
200
  protected readonly uiStateExpandedItems: _angular_core.WritableSignal<Record<string, boolean>>;
200
- private destroyer;
201
201
  private preferCollapse;
202
202
  constructor();
203
203
  ngOnChanges(changes: SimpleChanges): void;
204
204
  ngOnInit(): void;
205
- ngOnDestroy(): void;
206
205
  protected toggleCollapse(): void;
207
206
  /** Expands the vertical navbar. */
208
207
  expand(): void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@siemens/element-ng",
3
3
  "description": "Element Angular component library, implementing the Siemens Design Language",
4
- "version": "48.3.0",
4
+ "version": "48.5.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -30,8 +30,8 @@
30
30
  "@angular/router": "20",
31
31
  "@ngx-formly/bootstrap": "^6.2.2",
32
32
  "@ngx-formly/core": "^6.2.2",
33
- "@siemens/element-translate-ng": "48.3.0",
34
- "@siemens/element-theme": "48.3.0",
33
+ "@siemens/element-translate-ng": "48.5.0",
34
+ "@siemens/element-theme": "48.5.0",
35
35
  "@siemens/ngx-datatable": "22 - 24",
36
36
  "flag-icons": "^7.3.2",
37
37
  "google-libphonenumber": "^3.2.40",
@@ -71,14 +71,14 @@
71
71
  "types": "./accordion/index.d.ts",
72
72
  "default": "./fesm2022/siemens-element-ng-accordion.mjs"
73
73
  },
74
- "./about": {
75
- "types": "./about/index.d.ts",
76
- "default": "./fesm2022/siemens-element-ng-about.mjs"
77
- },
78
74
  "./action-modal": {
79
75
  "types": "./action-modal/index.d.ts",
80
76
  "default": "./fesm2022/siemens-element-ng-action-modal.mjs"
81
77
  },
78
+ "./about": {
79
+ "types": "./about/index.d.ts",
80
+ "default": "./fesm2022/siemens-element-ng-about.mjs"
81
+ },
82
82
  "./application-header": {
83
83
  "types": "./application-header/index.d.ts",
84
84
  "default": "./fesm2022/siemens-element-ng-application-header.mjs"
@@ -111,14 +111,14 @@
111
111
  "types": "./card/index.d.ts",
112
112
  "default": "./fesm2022/siemens-element-ng-card.mjs"
113
113
  },
114
- "./chat-messages": {
115
- "types": "./chat-messages/index.d.ts",
116
- "default": "./fesm2022/siemens-element-ng-chat-messages.mjs"
117
- },
118
114
  "./circle-status": {
119
115
  "types": "./circle-status/index.d.ts",
120
116
  "default": "./fesm2022/siemens-element-ng-circle-status.mjs"
121
117
  },
118
+ "./chat-messages": {
119
+ "types": "./chat-messages/index.d.ts",
120
+ "default": "./fesm2022/siemens-element-ng-chat-messages.mjs"
121
+ },
122
122
  "./color-picker": {
123
123
  "types": "./color-picker/index.d.ts",
124
124
  "default": "./fesm2022/siemens-element-ng-color-picker.mjs"
@@ -251,14 +251,14 @@
251
251
  "types": "./markdown-renderer/index.d.ts",
252
252
  "default": "./fesm2022/siemens-element-ng-markdown-renderer.mjs"
253
253
  },
254
- "./menu": {
255
- "types": "./menu/index.d.ts",
256
- "default": "./fesm2022/siemens-element-ng-menu.mjs"
257
- },
258
254
  "./modal": {
259
255
  "types": "./modal/index.d.ts",
260
256
  "default": "./fesm2022/siemens-element-ng-modal.mjs"
261
257
  },
258
+ "./menu": {
259
+ "types": "./menu/index.d.ts",
260
+ "default": "./fesm2022/siemens-element-ng-menu.mjs"
261
+ },
262
262
  "./navbar": {
263
263
  "types": "./navbar/index.d.ts",
264
264
  "default": "./fesm2022/siemens-element-ng-navbar.mjs"
@@ -29,7 +29,11 @@ export const actionModalMigrationRule = (options) => {
29
29
  continue;
30
30
  }
31
31
  const pendingTransformations = [];
32
+ const usedIdentifier = new Set();
32
33
  const visitNodeAndCollectTransformations = (node) => {
34
+ if (ts.isImportDeclaration(node)) {
35
+ return;
36
+ }
33
37
  // Collect method call transformations
34
38
  if (ts.isCallExpression(node)) {
35
39
  const methodTransformation = createActionDialogMethodCallTransformation(node);
@@ -42,11 +46,17 @@ export const actionModalMigrationRule = (options) => {
42
46
  const typeTransformation = createActionDialogTypeTransformation(node);
43
47
  if (typeTransformation) {
44
48
  pendingTransformations.push(typeTransformation);
49
+ // We remove the usage here. In order to avoid counting it as used identifier, we skip further processing.
50
+ return;
45
51
  }
46
52
  }
53
+ if (ts.isIdentifier(node)) {
54
+ usedIdentifier.add(node.text);
55
+ }
47
56
  node.forEachChild(visitNodeAndCollectTransformations);
48
57
  };
49
58
  sourceFile.forEachChild(visitNodeAndCollectTransformations);
59
+ pendingTransformations.push(...removeUnusedImports(sourceFile, usedIdentifier));
50
60
  if (pendingTransformations.length > 0) {
51
61
  applyCodeTransformations(tree, filePath, pendingTransformations);
52
62
  }
@@ -69,8 +79,7 @@ const createActionDialogMethodCallTransformation = (node) => {
69
79
  const newCode = printer.printNode(ts.EmitHint.Expression, newNode, node.getSourceFile());
70
80
  return {
71
81
  node,
72
- newCode,
73
- type: 'method-call'
82
+ newCode
74
83
  };
75
84
  };
76
85
  const createActionDialogTypeTransformation = (node) => {
@@ -81,8 +90,7 @@ const createActionDialogTypeTransformation = (node) => {
81
90
  }
82
91
  return {
83
92
  node,
84
- newCode: `'${matchingReplacement.new}'`,
85
- type: 'type-reference'
93
+ newCode: `'${matchingReplacement.new}'`
86
94
  };
87
95
  };
88
96
  /**
@@ -119,3 +127,36 @@ const applyCodeTransformations = (tree, filePath, codeTransformations) => {
119
127
  });
120
128
  tree.commitUpdate(recorder);
121
129
  };
130
+ const removeUnusedImports = (sourceFile, usedIdentifier) => {
131
+ const printer = ts.createPrinter();
132
+ const importFinder = (node) => {
133
+ if (ts.isImportDeclaration(node) &&
134
+ ts.isStringLiteral(node.moduleSpecifier) &&
135
+ /@(siemens|simpl)\/element-ng(\/action-modal)?/.test(node.moduleSpecifier.text) &&
136
+ node.importClause?.namedBindings &&
137
+ ts.isNamedImports(node.importClause.namedBindings)) {
138
+ const usedBindings = node.importClause.namedBindings.elements.filter(element =>
139
+ // This script anyway is not capable of handling aliasing imports, so we can ignore them here.
140
+ usedIdentifier.has(element.name.text) || !ACTION_MODAL_SYMBOLS.includes(element.name.text));
141
+ if (usedBindings.length === node.importClause.namedBindings.elements.length) {
142
+ // All bindings are used, no changes needed
143
+ return undefined;
144
+ }
145
+ if (usedBindings.length === 0) {
146
+ // No bindings are used, remove the entire import statement
147
+ return {
148
+ node,
149
+ newCode: ''
150
+ };
151
+ }
152
+ // Recreate import statement with only used bindings
153
+ const newImport = ts.factory.createImportDeclaration(node.modifiers, ts.factory.createImportClause(node.importClause.isTypeOnly, node.importClause.name, ts.factory.createNamedImports(usedBindings)), node.moduleSpecifier, node.attributes);
154
+ return {
155
+ node,
156
+ newCode: printer.printNode(ts.EmitHint.Unspecified, newImport, sourceFile)
157
+ };
158
+ }
159
+ return undefined;
160
+ };
161
+ return sourceFile.statements.map(importFinder).filter(transform => transform !== undefined);
162
+ };
@@ -2,7 +2,6 @@ export const OUTPUT_NAMES_MIGRATION = [
2
2
  {
3
3
  module: /@(siemens|simpl)\/element-ng(\/accordion)?/,
4
4
  elementSelector: 'si-collapsible-panel',
5
- componentOrModuleName: ['SiCollapsiblePanelComponent', 'SiAccordionModule'],
6
5
  apiMappings: [{ replace: '(toggle)', replaceWith: '(panelToggle)' }]
7
6
  }
8
7
  ];
@@ -2,57 +2,48 @@ export const SYMBOL_REMOVALS_MIGRATION = [
2
2
  {
3
3
  module: /@(siemens|simpl)\/element-ng(\/accordion)?/,
4
4
  elementSelector: 'si-accordion',
5
- componentOrModuleName: ['SiAccordionComponent', 'SiAccordionModule'],
6
5
  names: ['colorVariant']
7
6
  },
8
7
  {
9
8
  module: /@(siemens|simpl)\/element-ng(\/datepicker)?/,
10
9
  elementSelector: 'input',
11
10
  attributeSelector: 'siDateInput',
12
- componentOrModuleName: ['SiDateInputDirective', 'SiDatepickerModule'],
13
11
  names: ['dateInputDebounceTime']
14
12
  },
15
13
  {
16
14
  module: /@(siemens|simpl)\/element-ng(\/datepicker)?/,
17
15
  elementSelector: 'input',
18
16
  attributeSelector: 'siDatepicker',
19
- componentOrModuleName: ['SiDateInputDirective', 'SiDatepickerModule'],
20
17
  names: ['triggeringInput']
21
18
  },
22
19
  {
23
20
  module: /@(siemens|simpl)\/element-ng(\/datepicker)?/,
24
21
  elementSelector: 'si-date-range',
25
- componentOrModuleName: ['SiDateRangeComponent', 'SiDatepickerModule'],
26
22
  names: ['debounceTime']
27
23
  },
28
24
  {
29
25
  module: /@(siemens|simpl)\/element-ng(\/filtered-search)?/,
30
26
  elementSelector: 'si-filtered-search',
31
- componentOrModuleName: ['SiFilteredSearchComponent', 'SiFilteredSearchModule'],
32
27
  names: ['showIcon', 'noMatchingCriteriaText']
33
28
  },
34
29
  {
35
30
  module: /@(siemens|simpl)\/element-ng(\/form)?/,
36
31
  elementSelector: 'si-form-item',
37
- componentOrModuleName: ['SiFormItemComponent', 'SiFormModule'],
38
32
  names: ['inputId', 'readonly']
39
33
  },
40
34
  {
41
35
  module: /@(siemens|simpl)\/element-ng(\/navbar-vertical)?/,
42
36
  elementSelector: 'si-navbar-vertical',
43
- componentOrModuleName: ['SiNavbarVerticalComponent', 'SiNavbarVerticalModule'],
44
37
  names: ['autoCollapseDelay']
45
38
  },
46
39
  {
47
40
  module: /@(siemens|simpl)\/element-ng(\/split)?/,
48
41
  elementSelector: 'si-split-part',
49
- componentOrModuleName: ['SiSplitPartComponent', 'SiSplitModule'],
50
42
  names: ['headerStatusColor', 'headerStatusIconClass']
51
43
  },
52
44
  {
53
45
  module: /@(siemens|simpl)\/element-ng(\/navbar-vertical)?/,
54
46
  elementSelector: 'si-tree-view',
55
- componentOrModuleName: ['SiTreeViewComponent', 'SiTreeViewModule'],
56
47
  names: ['disableFilledIcons', 'trackByFunction']
57
48
  }
58
49
  ];
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import * as ts from 'typescript';
6
6
  import { EmitHint } from 'typescript';
7
- import { discoverSourceFiles, getImportSpecifiers, renameApi, renameAttribute, renameElementTag, renameIdentifier, removeSymbol } from '../../utils/index.js';
7
+ import { discoverSourceFiles, renameApi, renameAttribute, renameElementTag, renameIdentifier, removeSymbol } from '../../utils/index.js';
8
8
  import { getElementMigrationData } from '../data/index.js';
9
9
  export const elementMigrationRule = (options) => {
10
10
  return async (tree, context) => {
@@ -60,10 +60,6 @@ export const elementMigrationRule = (options) => {
60
60
  if (migrationData.outputNameChanges) {
61
61
  recorder ??= tree.beginUpdate(filePath);
62
62
  for (const change of migrationData.outputNameChanges) {
63
- const importSpecifiers = getImportSpecifiers(sourceFile, change.module, change.componentOrModuleName);
64
- if (!importSpecifiers?.length) {
65
- continue;
66
- }
67
63
  renameApi({
68
64
  tree,
69
65
  recorder,
@@ -77,10 +73,6 @@ export const elementMigrationRule = (options) => {
77
73
  if (migrationData.symbolRemovalChanges) {
78
74
  recorder ??= tree.beginUpdate(filePath);
79
75
  for (const change of migrationData.symbolRemovalChanges) {
80
- const importSpecifiers = getImportSpecifiers(sourceFile, change.module, change.componentOrModuleName);
81
- if (!importSpecifiers?.length) {
82
- continue;
83
- }
84
76
  removeSymbol({
85
77
  tree,
86
78
  recorder,
@@ -2,9 +2,9 @@
2
2
  * Copyright (c) Siemens 2016 - 2025
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
- import { join, dirname } from 'path';
5
+ import { join, dirname } from 'path/posix';
6
6
  import * as ts from 'typescript';
7
- import { discoverSourceFiles, findElement, getImportSpecifiers, getInlineTemplates, getTemplateUrl } from '../../utils/index.js';
7
+ import { discoverSourceFiles, findElement, getInlineTemplates, getTemplateUrl } from '../../utils/index.js';
8
8
  export const wizardMigrationRule = (options) => {
9
9
  return async (tree, context) => {
10
10
  context.logger.info('🔄 Migrating wizard api...');
@@ -15,14 +15,6 @@ export const wizardMigrationRule = (options) => {
15
15
  continue;
16
16
  }
17
17
  const sourceFile = ts.createSourceFile(filePath, content.toString(), ts.ScriptTarget.Latest, true);
18
- const modulePathToMatch = /@(siemens|simpl)\/element-ng(\/wizard)?/;
19
- const wizardImports = getImportSpecifiers(sourceFile, modulePathToMatch, [
20
- 'SiWizardComponent',
21
- 'SiWizardModule'
22
- ]);
23
- if (!wizardImports?.length) {
24
- continue;
25
- }
26
18
  const recorder = tree.beginUpdate(filePath);
27
19
  renameApi({
28
20
  tree,
@@ -3,8 +3,10 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
  import { chain } from '@angular-devkit/schematics';
6
+ import { getDecoratorMetadata, getMetadataField } from '@schematics/angular/utility/ast-utils';
7
+ import * as ts from 'typescript';
6
8
  import { getImportNodes, getSymbols, discoverSourceFiles } from '../utils/index.js';
7
- import { CHARTS_NG_MAPPINGS, DASHBOARDS_NG_MAPPINGS, ELEMENT_NG_MAPPINGS, ELEMENT_TRANSLATE_NG_MAPPINGS, MAPS_NG_MAPPINGS } from './mappings/index.js';
9
+ import { CHARTS_NG_MAPPINGS, DASHBOARDS_NG_MAPPINGS, ELEMENT_NG_MAPPINGS, ELEMENT_TRANSLATE_NG_MAPPINGS, MAPS_NG_MAPPINGS, NATIVE_CHARTS_NG_MAPPINGS, SIMPL_ELEMENT_NG_MODULES } from './mappings/index.js';
8
10
  export const tsImportMigration = (_options) => {
9
11
  return (tree, context) => {
10
12
  context.logger.info('🚀 Starting Simpl to Siemens migration...');
@@ -33,8 +35,10 @@ export const tsImportMigrationRule = (_options) => {
33
35
  const sourceFiles = await discoverSourceFiles(tree, context, _options.path);
34
36
  for (const filePath of sourceFiles) {
35
37
  const migrations = collectMigrationImports(filePath, tree, context);
36
- const { imports, toRemoveImports } = migrations;
37
- if (imports.size === 0 && toRemoveImports.length === 0) {
38
+ const { imports, toRemoveImports, hasSimplElementNgModuleImport } = migrations;
39
+ if (imports.size === 0 &&
40
+ toRemoveImports.length === 0 &&
41
+ hasSimplElementNgModuleImport === false) {
38
42
  continue;
39
43
  }
40
44
  const rule = rewriteImportsInFile(filePath, migrations);
@@ -43,57 +47,155 @@ export const tsImportMigrationRule = (_options) => {
43
47
  return chain([...rules]);
44
48
  };
45
49
  };
50
+ /**
51
+ * Adds a symbol to the imports map, avoiding duplicates
52
+ */
53
+ const addSymbolToImports = (imports, importPath, symbolName) => {
54
+ const existingSymbols = imports.get(importPath);
55
+ if (!existingSymbols) {
56
+ imports.set(importPath, [symbolName]);
57
+ }
58
+ else if (!existingSymbols.includes(symbolName)) {
59
+ existingSymbols.push(symbolName);
60
+ }
61
+ };
46
62
  const collectMigrationImports = (filePath, tree, _context) => {
47
63
  const content = tree.read(filePath);
48
64
  const imports = new Map();
65
+ let hasSimplElementNgModuleImport = false;
49
66
  if (!content) {
50
- return { imports, toRemoveImports: [] };
67
+ return {
68
+ imports,
69
+ toRemoveImports: [],
70
+ hasSimplElementNgModuleImport
71
+ };
51
72
  }
52
73
  const simplImports = getImportNodes(filePath, content.toString(), '@simpl/');
53
74
  const toRemoveImports = [];
54
75
  for (const node of simplImports) {
55
76
  const symbols = getSymbols(node);
56
77
  const importPath = node.moduleSpecifier.getText().replace(/['"]/g, '');
57
- let toRemove = false;
78
+ let shouldRemoveImport = false;
58
79
  for (const symbol of symbols) {
59
80
  const symbolName = symbol.name.getText();
60
- const newImportPath = findComponentImportPath(symbolName, importPath);
61
- if (!newImportPath) {
62
- continue;
63
- }
64
- toRemove = true;
65
- const migration = imports.get(newImportPath);
66
- if (!migration) {
67
- imports.set(newImportPath, [symbolName]);
81
+ if (symbolName === 'SimplElementNgModule') {
82
+ hasSimplElementNgModuleImport = true;
83
+ for (const moduleName of SIMPL_ELEMENT_NG_MODULES) {
84
+ const newImportPath = findComponentImportPath(moduleName, '@simpl/element-ng');
85
+ if (!newImportPath) {
86
+ continue;
87
+ }
88
+ shouldRemoveImport = true;
89
+ addSymbolToImports(imports, newImportPath, moduleName);
90
+ }
68
91
  }
69
92
  else {
70
- migration.push(symbolName);
93
+ const newImportPath = findComponentImportPath(symbolName, importPath);
94
+ if (!newImportPath) {
95
+ continue;
96
+ }
97
+ shouldRemoveImport = true;
98
+ addSymbolToImports(imports, newImportPath, symbolName);
71
99
  }
72
100
  }
73
- if (toRemove) {
101
+ if (shouldRemoveImport) {
74
102
  toRemoveImports.push(node);
75
103
  }
76
104
  }
77
- return { imports, toRemoveImports };
105
+ return { imports, toRemoveImports, hasSimplElementNgModuleImport };
78
106
  };
79
107
  const rewriteImportsInFile = (filePath, migrations) => {
80
108
  return (tree) => {
81
109
  const recorder = tree.beginUpdate(filePath);
82
- const start = migrations.toRemoveImports.at(0)?.getFullStart() ?? 0;
83
- // Remove old imports
84
- for (const importDecl of migrations.toRemoveImports) {
85
- recorder.remove(importDecl.getFullStart(), importDecl.getFullWidth());
110
+ const insertPosition = migrations.toRemoveImports.at(0)?.getFullStart() ?? 0;
111
+ const content = tree.read(filePath);
112
+ if (!content) {
113
+ return tree;
86
114
  }
115
+ const sourceFile = ts.createSourceFile(filePath, content.toString(), ts.ScriptTarget.Latest, true);
116
+ // Remove old imports
117
+ removeOldImports(recorder, migrations.toRemoveImports);
87
118
  // Add new imports
88
- const importPaths = Array.from(migrations.imports.keys()).sort((a, b) => a.localeCompare(b));
89
- for (const path of importPaths) {
90
- const symbols = migrations.imports.get(path);
91
- recorder.insertLeft(start, `\nimport { ${symbols.sort((a, b) => a.localeCompare(b)).join(', ')} } from '${path}';`);
119
+ addNewImports(recorder, migrations.imports, insertPosition, sourceFile);
120
+ // Update NgModule/Component imports array if needed
121
+ if (migrations.hasSimplElementNgModuleImport) {
122
+ updateDecoratorImports(recorder, sourceFile);
92
123
  }
93
124
  tree.commitUpdate(recorder);
94
125
  return tree;
95
126
  };
96
127
  };
128
+ const updateDecoratorImports = (recorder, sourceFile) => {
129
+ const decoratorNames = ['NgModule', 'Component'];
130
+ let decoratorNode;
131
+ // Try to find NgModule or Component decorator
132
+ for (const decoratorName of decoratorNames) {
133
+ const nodes = getDecoratorMetadata(sourceFile, decoratorName, '@angular/core');
134
+ if (nodes.length > 0) {
135
+ decoratorNode = nodes[0];
136
+ break;
137
+ }
138
+ }
139
+ if (!decoratorNode || !ts.isObjectLiteralExpression(decoratorNode)) {
140
+ return;
141
+ }
142
+ const matchingProperties = getMetadataField(decoratorNode, 'imports');
143
+ const importsAssignment = matchingProperties[0];
144
+ if (!ts.isPropertyAssignment(importsAssignment) ||
145
+ !ts.isArrayLiteralExpression(importsAssignment.initializer)) {
146
+ return;
147
+ }
148
+ const elements = importsAssignment.initializer.elements;
149
+ // Filter out SimplElementNgModule and get existing modules
150
+ const existingModules = elements
151
+ .filter(e => e.getText() !== 'SimplElementNgModule')
152
+ .map(e => e.getText());
153
+ const existingModulesSet = new Set(existingModules);
154
+ // Only add modules that don't already exist
155
+ const newModulesToAdd = SIMPL_ELEMENT_NG_MODULES.filter(m => !existingModulesSet.has(m));
156
+ // Combine existing (without SimplElementNgModule) + new modules
157
+ const allModules = [...existingModules, ...newModulesToAdd];
158
+ // Remove existing imports array
159
+ recorder.remove(importsAssignment.getFullStart(), importsAssignment.getFullWidth());
160
+ // Create and insert the updated property assignment
161
+ const printer = ts.createPrinter();
162
+ const newNode = ts.factory.createArrayLiteralExpression(allModules.map(m => ts.factory.createIdentifier(m)), true);
163
+ const newProperty = ts.factory.updatePropertyAssignment(importsAssignment, importsAssignment.name, newNode);
164
+ const propertyText = printer.printNode(ts.EmitHint.Unspecified, newProperty, sourceFile);
165
+ // Had to add extra indentation to align properly
166
+ recorder.insertLeft(importsAssignment.getStart(), `\n ` + propertyText);
167
+ };
168
+ /**
169
+ * Removes old import declarations from the file
170
+ */
171
+ const removeOldImports = (recorder, toRemoveImports) => {
172
+ for (const importDecl of toRemoveImports) {
173
+ recorder.remove(importDecl.getFullStart(), importDecl.getFullWidth());
174
+ }
175
+ };
176
+ /**
177
+ * Adds new import declarations to the file
178
+ */
179
+ const addNewImports = (recorder, imports, insertPosition, sourceFile) => {
180
+ const sortedImportPaths = Array.from(imports.keys()).sort((a, b) => a.localeCompare(b));
181
+ const printer = ts.createPrinter();
182
+ let counter = 0;
183
+ for (const path of sortedImportPaths) {
184
+ const symbols = imports.get(path);
185
+ const sortedSymbols = symbols.sort((a, b) => a.localeCompare(b));
186
+ const importDeclaration = createImportDeclaration(path, sortedSymbols);
187
+ const importStatement = printer.printNode(ts.EmitHint.Unspecified, importDeclaration, sourceFile);
188
+ const prefix = insertPosition === 0 && counter === 0 ? '' : '\n';
189
+ recorder.insertLeft(insertPosition, `${prefix}${importStatement}`);
190
+ counter++;
191
+ }
192
+ };
193
+ /**
194
+ * Creates import declarations using TypeScript AST
195
+ */
196
+ const createImportDeclaration = (importPath, symbols) => {
197
+ return ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports(symbols.map(symbol => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(symbol))))), ts.factory.createStringLiteral(importPath, true));
198
+ };
97
199
  const findComponentImportPath = (symbolName, moduleSpecifier) => {
98
200
  const [, project, subPath] = moduleSpecifier.split('/');
99
201
  switch (project) {
@@ -102,7 +204,8 @@ const findComponentImportPath = (symbolName, moduleSpecifier) => {
102
204
  if (symbolName === 'MenuItem' && subPath === 'menu') {
103
205
  return '@siemens/element-ng/menu';
104
206
  }
105
- return ELEMENT_NG_MAPPINGS[symbolName];
207
+ // Added the OR condition to check in ELEMENT_TRANSLATE_NG_MAPPINGS as well because translation are also part of `@simpl/element-ng` imports
208
+ return ELEMENT_NG_MAPPINGS[symbolName] || ELEMENT_TRANSLATE_NG_MAPPINGS[symbolName];
106
209
  }
107
210
  case 'maps-ng':
108
211
  return MAPS_NG_MAPPINGS[symbolName];
@@ -110,6 +213,8 @@ const findComponentImportPath = (symbolName, moduleSpecifier) => {
110
213
  return DASHBOARDS_NG_MAPPINGS[symbolName];
111
214
  case 'charts-ng':
112
215
  return CHARTS_NG_MAPPINGS[symbolName];
216
+ case 'native-charts-ng':
217
+ return NATIVE_CHARTS_NG_MAPPINGS[symbolName];
113
218
  case 'element-translate-ng':
114
219
  return ELEMENT_TRANSLATE_NG_MAPPINGS[symbolName];
115
220
  default:
@@ -66,5 +66,6 @@ export const CHARTS_NG_MAPPINGS = {
66
66
  'themeElement': '@siemens/charts-ng',
67
67
  'themeSupport': '@siemens/charts-ng',
68
68
  'XAxisPosition': '@siemens/charts-ng',
69
- 'YAxisPosition': '@siemens/charts-ng'
69
+ 'YAxisPosition': '@siemens/charts-ng',
70
+ 'SimplChartsNgModule': '@siemens/charts-ng'
70
71
  };
@@ -48,5 +48,7 @@ export const DASHBOARDS_NG_MAPPINGS = {
48
48
  'WidgetInstanceEditor': '@siemens/dashboards-ng',
49
49
  'WidgetInstanceEditorWizard': '@siemens/dashboards-ng',
50
50
  'WidgetInstanceEditorWizardState': '@siemens/dashboards-ng',
51
- 'WidgetPositionConfig': '@siemens/dashboards-ng'
51
+ 'WidgetPositionConfig': '@siemens/dashboards-ng',
52
+ 'SimplDashboardsNgModule': '@siemens/dashboards-ng',
53
+ 'SiWidgetStorage': '@siemens/dashboards-ng'
52
54
  };
@@ -473,6 +473,7 @@ export const ELEMENT_NG_MAPPINGS = {
473
473
  'SiLoadingSpinnerModule': '@siemens/element-ng/loading-spinner',
474
474
  'SiLocaleConfig': '@siemens/element-ng/localization',
475
475
  'SiLocaleId': '@siemens/element-ng/localization',
476
+ 'SiLocaleStore': '@siemens/element-ng/localization',
476
477
  'SiLocaleService': '@siemens/element-ng/localization',
477
478
  'SiLoginBasicComponent': '@siemens/element-ng/landing-page',
478
479
  'SiLoginSingleSignOnComponent': '@siemens/element-ng/landing-page',
@@ -649,3 +650,56 @@ export const ELEMENT_NG_MAPPINGS = {
649
650
  'WEEK_START_OFFSET': '@siemens/element-ng/datepicker',
650
651
  'WeekStart': '@siemens/element-ng/datepicker'
651
652
  };
653
+ export const SIMPL_ELEMENT_NG_MODULES = [
654
+ 'SiAboutModule',
655
+ 'SiAccordionModule',
656
+ 'SiBreadcrumbModule',
657
+ 'SiBreadcrumbRouterModule',
658
+ 'SiCardModule',
659
+ 'SiCircleStatusModule',
660
+ 'SiConnectionStrengthModule',
661
+ 'SiContentActionBarModule',
662
+ 'SiCopyrightNoticeModule',
663
+ 'SiDatepickerModule',
664
+ 'SiDateRangeFilterModule',
665
+ 'SiElectrontitlebarModule',
666
+ 'SiEmptyStateModule',
667
+ 'SiFileUploaderModule',
668
+ 'SiFilterBarModule',
669
+ 'SiFilteredSearchModule',
670
+ 'SiFooterModule',
671
+ 'SiFormModule',
672
+ 'SiIconModule',
673
+ 'SiIconStatusModule',
674
+ 'SiInlineNotificationModule',
675
+ 'SiLandingPageModule',
676
+ 'SiLanguageSwitcherModule',
677
+ 'SiLinkModule',
678
+ 'SiLoadingSpinnerModule',
679
+ 'SiMainDetailContainerModule',
680
+ 'SiNavbarModule',
681
+ 'SiNavbarVerticalModule',
682
+ 'SiNumberInputModule',
683
+ 'SiPaginationModule',
684
+ 'SiPasswordStrengthModule',
685
+ 'SiPasswordToggleModule',
686
+ 'SiPillsInputModule',
687
+ 'SiPopoverLegacyModule',
688
+ 'SiProgressbarModule',
689
+ 'SiResizeObserverModule',
690
+ 'SiResultDetailsListModule',
691
+ 'SiSearchBarModule',
692
+ 'SiSelectModule',
693
+ 'SiSidePanelModule',
694
+ 'SiSliderModule',
695
+ 'SiSortBarModule',
696
+ 'SiSplitModule',
697
+ 'SiStatusBarModule',
698
+ 'SiTabsLegacyModule',
699
+ 'SiThresholdModule',
700
+ 'SiTooltipModule',
701
+ 'SiTreeViewModule',
702
+ 'SiTypeaheadModule',
703
+ 'SiUnauthorizedPageModule',
704
+ 'SiWizardModule'
705
+ ];