@siemens/element-ng 48.4.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.
@@ -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
+ ];
@@ -16,6 +16,6 @@ export const ELEMENT_TRANSLATE_NG_MAPPINGS = {
16
16
  'SiTranslateNgxTModule': '@siemens/element-translate-ng/ngx-translate',
17
17
  'SiTranslatePipe': '@siemens/element-translate-ng/translate',
18
18
  't': '@siemens/element-translate-ng/translate',
19
- 'TranslatableString': '@siemens/element-translate-ng/translate-types',
19
+ 'TranslatableString': '@siemens/element-translate-ng/translate',
20
20
  'TranslationResult': '@siemens/element-translate-ng/translate'
21
21
  };
@@ -7,3 +7,4 @@ export * from './dashboards-ng-mappings.js';
7
7
  export * from './element-ng-mappings.js';
8
8
  export * from './element-translate-ng-mappings.js';
9
9
  export * from './maps-ng-mappings.js';
10
+ export * from './native-charts-ng-mappings.js';
@@ -42,5 +42,6 @@ export const MAPS_NG_MAPPINGS = {
42
42
  'SiMapPopoverComponent': '@siemens/maps-ng',
43
43
  'SiMapsNgModule': '@siemens/maps-ng',
44
44
  'SiMapTooltipComponent': '@siemens/maps-ng',
45
- 'TOOLTIP_FEATURES_TO_DISPLAY': '@siemens/maps-ng'
45
+ 'TOOLTIP_FEATURES_TO_DISPLAY': '@siemens/maps-ng',
46
+ 'SimplMapsNgModule': '@siemens/maps-ng'
46
47
  };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Copyright (c) Siemens 2016 - 2025
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ export const NATIVE_CHARTS_NG_MAPPINGS = {
6
+ // Components
7
+ 'SiNChartGaugeComponent': '@siemens/native-charts-ng/gauge',
8
+ 'SiMicrochartBarComponent': '@siemens/native-charts-ng/microchart-bar',
9
+ 'SiMicrochartDonutComponent': '@siemens/native-charts-ng/microchart-donut',
10
+ 'SiMicrochartLineComponent': '@siemens/native-charts-ng/microchart-line',
11
+ 'SiMicrochartProgressComponent': '@siemens/native-charts-ng/microchart-progress',
12
+ // Module
13
+ 'SiNativeChartsNgModule': '@siemens/native-charts-ng',
14
+ 'SimplNativeChartsNgModule': '@siemens/native-charts-ng',
15
+ // Interfaces - Gauge
16
+ 'GaugeSeries': '@siemens/native-charts-ng/gauge',
17
+ 'GaugeSegment': '@siemens/native-charts-ng/gauge',
18
+ // Interfaces - Microchart Bar
19
+ 'MicrochartBarSeries': '@siemens/native-charts-ng/microchart-bar',
20
+ // Interfaces - Microchart Donut
21
+ 'MicrochartDonutSeries': '@siemens/native-charts-ng/microchart-donut',
22
+ // Interfaces - Microchart Line
23
+ 'MicrochartLineSeries': '@siemens/native-charts-ng/microchart-line',
24
+ // Interfaces - Microchart Progress
25
+ 'MicrochartProgressSeries': '@siemens/native-charts-ng/microchart-progress',
26
+ // Utilities
27
+ 'Coordinate': '@siemens/native-charts-ng/utils',
28
+ 'polarToCartesian': '@siemens/native-charts-ng/utils',
29
+ 'makeArc': '@siemens/native-charts-ng/utils',
30
+ 'makeLine': '@siemens/native-charts-ng/utils',
31
+ 'makePolyline': '@siemens/native-charts-ng/utils',
32
+ 'valueToRelativeAngle': '@siemens/native-charts-ng/utils'
33
+ };
@@ -5,7 +5,7 @@
5
5
  import { normalize } from '@angular-devkit/core';
6
6
  import { SchematicsException } from '@angular-devkit/schematics';
7
7
  import { allTargetOptions, allWorkspaceTargets, getWorkspace } from '@schematics/angular/utility/workspace';
8
- import { dirname, isAbsolute, resolve } from 'path';
8
+ import { dirname, isAbsolute, resolve } from 'path/posix';
9
9
  import { parseTsconfigFile } from './ts-utils.js';
10
10
  export const getGlobalStyles = async (tree) => {
11
11
  const globalStyles = new Set();
@@ -3,7 +3,7 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
  import { normalize } from '@angular-devkit/core';
6
- import { relative } from 'path';
6
+ import { relative } from 'path/posix';
7
7
  export class SchematicsFileSystem {
8
8
  tree;
9
9
  basePath = normalize(process.cwd());
@@ -1,4 +1,4 @@
1
- import { join, dirname } from 'path';
1
+ import { join, dirname } from 'path/posix';
2
2
  import ts from 'typescript';
3
3
  import { findAttribute, findElement } from './html-utils.js';
4
4
  export const getInlineTemplates = (source) => {
@@ -100,7 +100,7 @@ const removeSymbols = ({ template, offset, recorder, elementName, attributeSelec
100
100
  };
101
101
  export const renameElementTag = ({ tree, filePath, sourceFile, recorder, fromName, toName }) => {
102
102
  getInlineTemplates(sourceFile).forEach(template => renameElementTagInTemplate({
103
- template: template.text,
103
+ template: sourceFile.text.substring(template.getStart() + 1, template.getEnd() - 1),
104
104
  offset: template.getStart() + 1,
105
105
  toName,
106
106
  fromName,
@@ -122,7 +122,7 @@ export const renameElementTag = ({ tree, filePath, sourceFile, recorder, fromNam
122
122
  };
123
123
  export const renameAttribute = ({ tree, filePath, sourceFile, recorder, fromName, toName }) => {
124
124
  getInlineTemplates(sourceFile).forEach(template => renameAttributeInTemplate({
125
- template: template.text,
125
+ template: sourceFile.text.substring(template.getStart() + 1, template.getEnd() - 1),
126
126
  offset: template.getStart() + 1,
127
127
  toName,
128
128
  fromName,
@@ -144,7 +144,7 @@ export const renameAttribute = ({ tree, filePath, sourceFile, recorder, fromName
144
144
  };
145
145
  export const renameApi = ({ tree, filePath, sourceFile, recorder, elementName, apis }) => {
146
146
  getInlineTemplates(sourceFile).forEach(template => renameApiInTemplate({
147
- template: template.text,
147
+ template: sourceFile.text.substring(template.getStart() + 1, template.getEnd() - 1),
148
148
  offset: template.getStart() + 1,
149
149
  elementName,
150
150
  recorder,
@@ -166,7 +166,7 @@ export const renameApi = ({ tree, filePath, sourceFile, recorder, elementName, a
166
166
  };
167
167
  export const removeSymbol = ({ tree, filePath, sourceFile, recorder, elementName, attributeSelector, names }) => {
168
168
  getInlineTemplates(sourceFile).forEach(template => removeSymbols({
169
- template: template.text,
169
+ template: sourceFile.text.substring(template.getStart() + 1, template.getEnd() - 1),
170
170
  offset: template.getStart() + 1,
171
171
  elementName,
172
172
  attributeSelector,
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { normalize } from '@angular-devkit/core';
6
6
  import { SchematicsException } from '@angular-devkit/schematics';
7
- import { isAbsolute } from 'path';
7
+ import { isAbsolute } from 'path/posix';
8
8
  import ts from 'typescript';
9
9
  import { SchematicsFileSystem } from './schematics-file-system.js';
10
10
  /**