@siemens/element-ng 48.1.0 → 48.2.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 (104) hide show
  1. package/README.md +5 -0
  2. package/card/index.d.ts +68 -29
  3. package/common/index.d.ts +11 -0
  4. package/dashboard/index.d.ts +1 -0
  5. package/datepicker/index.d.ts +30 -34
  6. package/fesm2022/siemens-element-ng-breadcrumb.mjs +2 -2
  7. package/fesm2022/siemens-element-ng-breadcrumb.mjs.map +1 -1
  8. package/fesm2022/siemens-element-ng-card.mjs +103 -37
  9. package/fesm2022/siemens-element-ng-card.mjs.map +1 -1
  10. package/fesm2022/siemens-element-ng-common.mjs +6 -0
  11. package/fesm2022/siemens-element-ng-common.mjs.map +1 -1
  12. package/fesm2022/siemens-element-ng-connection-strength.mjs +2 -2
  13. package/fesm2022/siemens-element-ng-connection-strength.mjs.map +1 -1
  14. package/fesm2022/siemens-element-ng-content-action-bar.mjs +2 -2
  15. package/fesm2022/siemens-element-ng-content-action-bar.mjs.map +1 -1
  16. package/fesm2022/siemens-element-ng-dashboard.mjs +10 -8
  17. package/fesm2022/siemens-element-ng-dashboard.mjs.map +1 -1
  18. package/fesm2022/siemens-element-ng-datatable.mjs +5 -0
  19. package/fesm2022/siemens-element-ng-datatable.mjs.map +1 -1
  20. package/fesm2022/siemens-element-ng-date-range-filter.mjs +1 -1
  21. package/fesm2022/siemens-element-ng-date-range-filter.mjs.map +1 -1
  22. package/fesm2022/siemens-element-ng-datepicker.mjs +173 -151
  23. package/fesm2022/siemens-element-ng-datepicker.mjs.map +1 -1
  24. package/fesm2022/siemens-element-ng-file-uploader.mjs +2 -2
  25. package/fesm2022/siemens-element-ng-file-uploader.mjs.map +1 -1
  26. package/fesm2022/siemens-element-ng-filter-bar.mjs +5 -5
  27. package/fesm2022/siemens-element-ng-filter-bar.mjs.map +1 -1
  28. package/fesm2022/siemens-element-ng-filtered-search.mjs +15 -3
  29. package/fesm2022/siemens-element-ng-filtered-search.mjs.map +1 -1
  30. package/fesm2022/siemens-element-ng-form.mjs +7 -1
  31. package/fesm2022/siemens-element-ng-form.mjs.map +1 -1
  32. package/fesm2022/siemens-element-ng-formly.mjs +2 -2
  33. package/fesm2022/siemens-element-ng-formly.mjs.map +1 -1
  34. package/fesm2022/siemens-element-ng-header-dropdown.mjs +13 -1
  35. package/fesm2022/siemens-element-ng-header-dropdown.mjs.map +1 -1
  36. package/fesm2022/siemens-element-ng-ip-input.mjs +62 -28
  37. package/fesm2022/siemens-element-ng-ip-input.mjs.map +1 -1
  38. package/fesm2022/siemens-element-ng-language-switcher.mjs +1 -1
  39. package/fesm2022/siemens-element-ng-language-switcher.mjs.map +1 -1
  40. package/fesm2022/siemens-element-ng-list-details.mjs +2 -2
  41. package/fesm2022/siemens-element-ng-list-details.mjs.map +1 -1
  42. package/fesm2022/siemens-element-ng-navbar-vertical.mjs +1 -1
  43. package/fesm2022/siemens-element-ng-navbar-vertical.mjs.map +1 -1
  44. package/fesm2022/siemens-element-ng-pagination.mjs +2 -2
  45. package/fesm2022/siemens-element-ng-pagination.mjs.map +1 -1
  46. package/fesm2022/siemens-element-ng-photo-upload.mjs +1 -1
  47. package/fesm2022/siemens-element-ng-photo-upload.mjs.map +1 -1
  48. package/fesm2022/siemens-element-ng-search-bar.mjs +14 -4
  49. package/fesm2022/siemens-element-ng-search-bar.mjs.map +1 -1
  50. package/fesm2022/siemens-element-ng-side-panel.mjs +2 -2
  51. package/fesm2022/siemens-element-ng-side-panel.mjs.map +1 -1
  52. package/fesm2022/siemens-element-ng-status-bar.mjs +2 -2
  53. package/fesm2022/siemens-element-ng-status-bar.mjs.map +1 -1
  54. package/fesm2022/siemens-element-ng-tabs-legacy.mjs +2 -2
  55. package/fesm2022/siemens-element-ng-tabs-legacy.mjs.map +1 -1
  56. package/fesm2022/siemens-element-ng-tabs.mjs +5 -5
  57. package/fesm2022/siemens-element-ng-tabs.mjs.map +1 -1
  58. package/fesm2022/siemens-element-ng-tooltip.mjs +5 -6
  59. package/fesm2022/siemens-element-ng-tooltip.mjs.map +1 -1
  60. package/fesm2022/siemens-element-ng-translate.mjs.map +1 -1
  61. package/fesm2022/siemens-element-ng-tree-view.mjs +4 -4
  62. package/fesm2022/siemens-element-ng-tree-view.mjs.map +1 -1
  63. package/fesm2022/siemens-element-ng-typeahead.mjs +329 -257
  64. package/fesm2022/siemens-element-ng-typeahead.mjs.map +1 -1
  65. package/filter-bar/index.d.ts +9 -3
  66. package/header-dropdown/index.d.ts +7 -0
  67. package/ip-input/index.d.ts +42 -5
  68. package/package.json +23 -19
  69. package/schematics/collection.json +34 -0
  70. package/schematics/migrations/action-modal-migration/action-modal-migration.js +121 -0
  71. package/schematics/migrations/action-modal-migration/action-modal.mappings.js +98 -0
  72. package/schematics/migrations/action-modal-migration/index.js +5 -0
  73. package/schematics/migrations/index.js +13 -0
  74. package/schematics/migrations/schema.json +16 -0
  75. package/schematics/migrations/to-legacy-migration/to-legacy-migration.js +55 -0
  76. package/schematics/migrations/to-legacy-migration/to-legacy-replacement.js +35 -0
  77. package/schematics/ng-add/index.js +16 -0
  78. package/schematics/ng-add/schema.json +16 -0
  79. package/schematics/scss-import-to-siemens-migration/index.js +101 -0
  80. package/schematics/scss-import-to-siemens-migration/schema.json +16 -0
  81. package/schematics/scss-import-to-siemens-migration/style-mappings.js +46 -0
  82. package/schematics/simpl-siemens-migration/index.js +18 -0
  83. package/schematics/simpl-siemens-migration/schema.json +16 -0
  84. package/schematics/ts-import-to-siemens-migration/index.js +118 -0
  85. package/schematics/ts-import-to-siemens-migration/mappings/charts-ng-mappings.js +70 -0
  86. package/schematics/ts-import-to-siemens-migration/mappings/dashboards-ng-mappings.js +52 -0
  87. package/schematics/ts-import-to-siemens-migration/mappings/element-ng-mappings.js +651 -0
  88. package/schematics/ts-import-to-siemens-migration/mappings/element-translate-ng-mappings.js +21 -0
  89. package/schematics/ts-import-to-siemens-migration/mappings/index.js +9 -0
  90. package/schematics/ts-import-to-siemens-migration/mappings/maps-ng-mappings.js +46 -0
  91. package/schematics/ts-import-to-siemens-migration/model.js +4 -0
  92. package/schematics/ts-import-to-siemens-migration/schema.json +16 -0
  93. package/schematics/utils/html-utils.js +72 -0
  94. package/schematics/utils/index.js +10 -0
  95. package/schematics/utils/project-utils.js +75 -0
  96. package/schematics/utils/schematics-file-system.js +22 -0
  97. package/schematics/utils/template-utils.js +114 -0
  98. package/schematics/utils/testing.js +41 -0
  99. package/schematics/utils/ts-utils.js +195 -0
  100. package/search-bar/index.d.ts +11 -1
  101. package/template-i18n.json +7 -0
  102. package/tooltip/index.d.ts +1 -1
  103. package/translate/index.d.ts +7 -0
  104. package/typeahead/index.d.ts +85 -4
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Copyright (c) Siemens 2016 - 2025
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ import { SchematicsException } from '@angular-devkit/schematics';
6
+ import * as ts from 'typescript';
7
+ import { discoverSourceFiles, getImportSpecifiers } from '../../utils/index.js';
8
+ import { ACTION_DIALOG_TYPES_REPLACEMENTS, ACTION_MODAL_SYMBOLS, DIALOG_METHOD_CONFIGS, LEGACY_METHODS } from './action-modal.mappings.js';
9
+ /**
10
+ * Migration rule for action modal methods.
11
+ * @param options - The options object containing the path to the project files to be migrated.
12
+ * @returns A schematic rule for migrating action modal methods.
13
+ */
14
+ export const actionModalMigrationRule = (options) => {
15
+ return (tree, context) => {
16
+ context.logger.info('🔄 Migrating action modal methods to v48...');
17
+ const tsSourceFiles = discoverSourceFiles(tree, context, options.path);
18
+ for (const filePath of tsSourceFiles) {
19
+ const content = tree.read(filePath);
20
+ if (!content) {
21
+ continue;
22
+ }
23
+ const sourceFile = ts.createSourceFile(filePath, content.toString(), ts.ScriptTarget.Latest, true);
24
+ // Match both @siemens/element-ng and @simpl/element-ng, with or without /action-modal
25
+ // This ensures we only process files that import from these modules
26
+ const modulePathToMatch = /@(siemens|simpl)\/element-ng(\/action-modal)?/;
27
+ const actionModalImports = getImportSpecifiers(sourceFile, modulePathToMatch, ACTION_MODAL_SYMBOLS);
28
+ if (!actionModalImports?.length) {
29
+ continue;
30
+ }
31
+ const pendingTransformations = [];
32
+ const visitNodeAndCollectTransformations = (node) => {
33
+ // Collect method call transformations
34
+ if (ts.isCallExpression(node)) {
35
+ const methodTransformation = createActionDialogMethodCallTransformation(node);
36
+ if (methodTransformation) {
37
+ pendingTransformations.push(methodTransformation);
38
+ }
39
+ }
40
+ // Collect type reference transformations
41
+ if (ts.isPropertyAccessExpression(node)) {
42
+ const typeTransformation = createActionDialogTypeTransformation(node);
43
+ if (typeTransformation) {
44
+ pendingTransformations.push(typeTransformation);
45
+ }
46
+ }
47
+ node.forEachChild(visitNodeAndCollectTransformations);
48
+ };
49
+ sourceFile.forEachChild(visitNodeAndCollectTransformations);
50
+ if (pendingTransformations.length > 0) {
51
+ applyCodeTransformations(tree, filePath, pendingTransformations);
52
+ }
53
+ }
54
+ return tree;
55
+ };
56
+ };
57
+ const createActionDialogMethodCallTransformation = (node) => {
58
+ const expression = node.expression;
59
+ if (!ts.isPropertyAccessExpression(expression)) {
60
+ return null;
61
+ }
62
+ const methodName = expression.name.text;
63
+ if (!LEGACY_METHODS.includes(methodName)) {
64
+ return null;
65
+ }
66
+ const dialogPropertiesArray = generateDialogProperties(methodName, node.arguments);
67
+ const printer = ts.createPrinter();
68
+ const newNode = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(expression.expression, ts.factory.createIdentifier('showActionDialog')), undefined, [ts.factory.createObjectLiteralExpression(dialogPropertiesArray)]);
69
+ const newCode = printer.printNode(ts.EmitHint.Expression, newNode, node.getSourceFile());
70
+ return {
71
+ node,
72
+ newCode,
73
+ type: 'method-call'
74
+ };
75
+ };
76
+ const createActionDialogTypeTransformation = (node) => {
77
+ const nodeText = node.getText().trim();
78
+ const matchingReplacement = ACTION_DIALOG_TYPES_REPLACEMENTS.find(typeReplacement => nodeText === typeReplacement.old);
79
+ if (!matchingReplacement) {
80
+ return null;
81
+ }
82
+ return {
83
+ node,
84
+ newCode: `'${matchingReplacement.new}'`,
85
+ type: 'type-reference'
86
+ };
87
+ };
88
+ /**
89
+ * Generates the dialog properties for the action dialog.
90
+ * @param methodName - The name of the method being migrated.
91
+ * @param nodeArguments - The arguments passed to the method.
92
+ * @returns An array of PropertyAssignment nodes for TypeScript printer.
93
+ * @throws SchematicsException if the method configuration is unknown.
94
+ */
95
+ const generateDialogProperties = (methodName, nodeArguments) => {
96
+ const methodConfig = DIALOG_METHOD_CONFIGS[methodName];
97
+ if (!methodConfig) {
98
+ throw new SchematicsException(`Unknown method configuration for: ${methodName}`);
99
+ }
100
+ const dialogProperties = [
101
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('type'), ts.factory.createStringLiteral(methodConfig.type, true))
102
+ ];
103
+ nodeArguments.forEach((arg, index) => {
104
+ const paramName = methodConfig.parameters[index];
105
+ if (paramName) {
106
+ dialogProperties.push(ts.factory.createPropertyAssignment(ts.factory.createIdentifier(paramName), arg));
107
+ }
108
+ });
109
+ return dialogProperties;
110
+ };
111
+ const applyCodeTransformations = (tree, filePath, codeTransformations) => {
112
+ const recorder = tree.beginUpdate(filePath);
113
+ // Sort by position (descending) to avoid offset issues
114
+ codeTransformations
115
+ .sort((a, b) => b.node.getStart() - a.node.getStart())
116
+ .forEach(({ node, newCode }) => {
117
+ recorder.remove(node.getStart(), node.getWidth());
118
+ recorder.insertLeft(node.getStart(), newCode);
119
+ });
120
+ tree.commitUpdate(recorder);
121
+ };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Copyright (c) Siemens 2016 - 2025
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ export const ACTION_MODAL_SYMBOLS = [
6
+ 'AlertDialogResult',
7
+ 'ConfirmationDialogResult',
8
+ 'DeleteConfirmationDialogResult',
9
+ 'EditDiscardDialogResult',
10
+ 'SiActionDialogService'
11
+ ];
12
+ export const LEGACY_METHODS = [
13
+ 'showAlertDialog',
14
+ 'showConfirmationDialog',
15
+ 'showEditDiscardDialog',
16
+ 'showDeleteConfirmationDialog'
17
+ ];
18
+ export const DIALOG_METHOD_CONFIGS = {
19
+ showAlertDialog: {
20
+ type: 'alert',
21
+ parameters: ['message', 'heading', 'confirmBtnName', 'translationParams', 'icon', 'diOptions']
22
+ },
23
+ showConfirmationDialog: {
24
+ type: 'confirmation',
25
+ parameters: [
26
+ 'message',
27
+ 'heading',
28
+ 'confirmBtnName',
29
+ 'declineBtnName',
30
+ 'translationParams',
31
+ 'icon',
32
+ 'diOptions'
33
+ ]
34
+ },
35
+ showEditDiscardDialog: {
36
+ type: 'edit-discard',
37
+ parameters: [
38
+ 'disableSave',
39
+ 'message',
40
+ 'heading',
41
+ 'saveBtnName',
42
+ 'discardBtnName',
43
+ 'cancelBtnName',
44
+ 'disableSaveMessage',
45
+ 'disableSaveDiscardBtnName',
46
+ 'translationParams',
47
+ 'icon',
48
+ 'diOptions'
49
+ ]
50
+ },
51
+ showDeleteConfirmationDialog: {
52
+ type: 'delete-confirm',
53
+ parameters: [
54
+ 'message',
55
+ 'heading',
56
+ 'deleteBtnName',
57
+ 'cancelBtnName',
58
+ 'translationParams',
59
+ 'icon',
60
+ 'diOptions'
61
+ ]
62
+ }
63
+ };
64
+ export const ACTION_DIALOG_TYPES_REPLACEMENTS = [
65
+ // Alert dialog
66
+ {
67
+ old: 'AlertDialogResult.Confirm',
68
+ new: 'confirm'
69
+ },
70
+ // Edit discard dialog
71
+ {
72
+ old: 'EditDiscardDialogResult.Save',
73
+ new: 'save'
74
+ },
75
+ {
76
+ old: 'EditDiscardDialogResult.Discard',
77
+ new: 'discard'
78
+ },
79
+ {
80
+ old: 'EditDiscardDialogResult.Cancel',
81
+ new: 'cancel'
82
+ },
83
+ // Confirmation dialog
84
+ {
85
+ old: 'ConfirmationDialogResult.Confirm',
86
+ new: 'confirm'
87
+ },
88
+ { old: 'ConfirmationDialogResult.Decline', new: 'decline' },
89
+ // Delete confirmation dialog
90
+ {
91
+ old: 'DeleteConfirmationDialogResult.Delete',
92
+ new: 'delete'
93
+ },
94
+ {
95
+ old: 'DeleteConfirmationDialogResult.Cancel',
96
+ new: 'cancel'
97
+ }
98
+ ];
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Copyright (c) Siemens 2016 - 2025
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ export * from './action-modal-migration.js';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Copyright (c) Siemens 2016 - 2025
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ import { chain } from '@angular-devkit/schematics';
6
+ import { actionModalMigrationRule } from './action-modal-migration/index.js';
7
+ import { toLegacyMigrationRule } from './to-legacy-migration/to-legacy-migration';
8
+ export const v47to48Migration = (options) => {
9
+ return (tree, context) => {
10
+ context.logger.info('🚀 Starting migration from v47 to v48...');
11
+ return chain([actionModalMigrationRule(options), toLegacyMigrationRule(options)])(tree, context);
12
+ };
13
+ };
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema",
3
+ "$id": "v47to48MigrationSchema",
4
+ "title": "Siemens Element v47 to v48 migration",
5
+ "type": "object",
6
+ "properties": {
7
+ "path": {
8
+ "type": "string",
9
+ "description": "Path to the directory where the migration should be applied.",
10
+ "x-prompt": "Which directory do you want to migrate?",
11
+ "format": "path",
12
+ "default": "/"
13
+ }
14
+ },
15
+ "required": []
16
+ }
@@ -0,0 +1,55 @@
1
+ import * as ts from 'typescript';
2
+ import { EmitHint } from 'typescript';
3
+ import { discoverSourceFiles, renameAttribute, renameElementTag, renameIdentifier } from '../../utils/index.js';
4
+ import { IDENTIFIER_RENAMING_INSTRUCTIONS, ELEMENT_RENAMING_INSTRUCTIONS, ATTRIBUTE_RENAMING_INSTRUCTIONS } from './to-legacy-replacement.js';
5
+ export const toLegacyMigrationRule = (options) => {
6
+ return (tree, context) => {
7
+ context.logger.info('🔄 Running legacy migration rule...');
8
+ const tsSourceFiles = discoverSourceFiles(tree, context, options.path);
9
+ for (const filePath of tsSourceFiles) {
10
+ const content = tree.read(filePath);
11
+ if (!content) {
12
+ continue;
13
+ }
14
+ const sourceFile = ts.createSourceFile(filePath, content.toString(), ts.ScriptTarget.Latest, true);
15
+ const changeInstructions = renameIdentifier({
16
+ sourceFile,
17
+ renamingInstructions: IDENTIFIER_RENAMING_INSTRUCTIONS
18
+ });
19
+ let recorder = undefined;
20
+ let printer = undefined;
21
+ for (const changeInstruction of changeInstructions) {
22
+ recorder ??= tree.beginUpdate(filePath);
23
+ printer ??= ts.createPrinter();
24
+ recorder.remove(changeInstruction.start, changeInstruction.width);
25
+ recorder.insertLeft(changeInstruction.start, printer.printNode(EmitHint.Unspecified, changeInstruction.newNode, sourceFile));
26
+ }
27
+ if (!recorder) {
28
+ continue;
29
+ }
30
+ for (const [fromName, toName] of ELEMENT_RENAMING_INSTRUCTIONS) {
31
+ renameElementTag({
32
+ tree,
33
+ recorder,
34
+ sourceFile,
35
+ filePath,
36
+ fromName,
37
+ toName
38
+ });
39
+ }
40
+ for (const [fromName, toName] of ATTRIBUTE_RENAMING_INSTRUCTIONS) {
41
+ renameAttribute({
42
+ tree,
43
+ recorder,
44
+ sourceFile,
45
+ filePath,
46
+ fromName,
47
+ toName
48
+ });
49
+ }
50
+ tree.commitUpdate(recorder);
51
+ }
52
+ context.logger.info(`✅ Legacy migration complete!`);
53
+ return tree;
54
+ };
55
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Copyright (c) Siemens 2016 - 2025
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ export const IDENTIFIER_RENAMING_INSTRUCTIONS = [
6
+ {
7
+ module: /@(siemens|simpl)\/element-ng(\/icon)?/,
8
+ symbolRenamings: [['SiIconComponent', 'SiIconLegacyComponent']]
9
+ },
10
+ {
11
+ module: /@(siemens|simpl)\/element-ng(\/tabs)?/,
12
+ symbolRenamings: [
13
+ ['SiTabComponent', 'SiTabLegacyComponent'],
14
+ ['SiTabsetComponent', 'SiTabsetLegacyComponent'],
15
+ ['SiTabsModule', 'SiTabsLegacyModule']
16
+ ],
17
+ toModule: '@siemens/element-ng/tabs-legacy'
18
+ },
19
+ {
20
+ module: /@(siemens|simpl)\/element-ng(\/popover)?/,
21
+ symbolRenamings: [
22
+ ['SiPopoverDirective', 'SiPopoverLegacyDirective'],
23
+ ['SiPopoverModule', 'SiPopoverLegacyModule']
24
+ ],
25
+ toModule: '@siemens/element-ng/popover-legacy'
26
+ }
27
+ ];
28
+ export const ELEMENT_RENAMING_INSTRUCTIONS = [
29
+ ['si-icon', 'si-icon-legacy'],
30
+ ['si-tabset', 'si-tabset-legacy'],
31
+ ['si-tab', 'si-tab-legacy']
32
+ ];
33
+ export const ATTRIBUTE_RENAMING_INSTRUCTIONS = [
34
+ ['siPopover', 'siPopoverLegacy']
35
+ ];
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Copyright (c) Siemens 2016 - 2025
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ import { chain, schematic } from '@angular-devkit/schematics';
6
+ import { getPackageJsonDependency } from '@schematics/angular/utility/dependencies';
7
+ export const ngAdd = (options) => {
8
+ return (tree, context) => {
9
+ context.logger.info('🔧 Adding @siemens/element-ng to your project...');
10
+ const hasSimplElementNgDependency = getPackageJsonDependency(tree, '@simpl/element-ng');
11
+ if (hasSimplElementNgDependency) {
12
+ const chainedRules = chain([schematic('simpl-siemens-migration', options)]);
13
+ return chainedRules(tree, context);
14
+ }
15
+ };
16
+ };
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema",
3
+ "$id": "siemens-element-ng-add",
4
+ "title": "Siemens Element ng-add schematic",
5
+ "type": "object",
6
+ "properties": {
7
+ "path": {
8
+ "type": "string",
9
+ "description": "Path to the directory where all simpl imports should be migrated.",
10
+ "x-prompt": "Which directory do you want to migrate?",
11
+ "format": "path",
12
+ "default": "/"
13
+ }
14
+ },
15
+ "required": []
16
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Copyright (c) Siemens 2016 - 2025
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ import { chain } from '@angular-devkit/schematics';
6
+ import { getGlobalStyles, discoverSourceFiles } from '../utils/index.js';
7
+ import { SCSS_USE_PATTERNS, STYLE_REPLACEMENTS, THEME_STYLE_ENTRIES } from './style-mappings.js';
8
+ export const scssImportMigration = (_options) => {
9
+ return (tree, context) => scssMigrationRule(_options)(tree, context);
10
+ };
11
+ /**
12
+ * Creates a schematic rule for migrating SCSS files in a Siemens migration context.
13
+ *
14
+ * This rule performs the following migration tasks:
15
+ * - Processes global styles files (`.scss` and `.sass`) and removes specific SCSS use patterns
16
+ * - Applies theme style entries to global styles based on predefined patterns
17
+ * - Discovers and migrates SCSS source files that contain specific style replacements
18
+ *
19
+ * @param _options - Migration options configuration (currently unused)
20
+ * @returns A schematic rule function that processes the file tree and applies SCSS migrations
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * const rule = scssMigrationRule({ path: 'some-path' });
25
+ * // Use with Angular Schematics
26
+ * ```
27
+ */
28
+ export const scssMigrationRule = (_options) => {
29
+ return (tree, context) => {
30
+ const rules = [];
31
+ context.logger.info('🎨 Migrating SCSS styles...');
32
+ const globalStyles = getGlobalStyles(tree);
33
+ for (const style of globalStyles) {
34
+ if (style.endsWith('.scss') || style.endsWith('.sass')) {
35
+ const content = tree.readText(style);
36
+ for (const pattern of SCSS_USE_PATTERNS) {
37
+ const match = pattern.exec(content);
38
+ if (match) {
39
+ rules.push(migrateScssImports(style, [{ replace: match[0], new: '' }]));
40
+ }
41
+ }
42
+ let predecessor = '';
43
+ for (const themeEntry of THEME_STYLE_ENTRIES) {
44
+ const match = content.match(themeEntry.pattern ?? themeEntry.insert);
45
+ if (match) {
46
+ predecessor = match[0];
47
+ continue;
48
+ }
49
+ rules.push(applyGlobalStyles(style, predecessor, themeEntry.insert + '\n'));
50
+ predecessor = themeEntry.insert;
51
+ }
52
+ }
53
+ }
54
+ const scssFiles = discoverSourceFiles(tree, context, _options.path, '.scss');
55
+ for (const filePath of scssFiles) {
56
+ const content = tree.readText(filePath);
57
+ if (content.includes(STYLE_REPLACEMENTS[0].replace) ||
58
+ content.includes(STYLE_REPLACEMENTS[1].replace)) {
59
+ rules.push(migrateScssImports(filePath));
60
+ }
61
+ }
62
+ return chain([...rules]);
63
+ };
64
+ };
65
+ const migrateScssImports = (filePath, replacements = STYLE_REPLACEMENTS) => {
66
+ return (tree) => {
67
+ const recorder = tree.beginUpdate(filePath);
68
+ const content = tree.readText(filePath);
69
+ let offset = 0;
70
+ let hasMore = true;
71
+ while (hasMore) {
72
+ hasMore = false;
73
+ for (const replacement of replacements) {
74
+ const start = content.indexOf(replacement.replace, offset);
75
+ if (start >= 0) {
76
+ recorder.remove(start, replacement.replace.length);
77
+ recorder.insertLeft(start, replacement.new);
78
+ const size = replacement.new.length === 0 ? 1 : replacement.new.length;
79
+ offset = start + size;
80
+ hasMore = true;
81
+ }
82
+ }
83
+ }
84
+ tree.commitUpdate(recorder);
85
+ return tree;
86
+ };
87
+ };
88
+ const applyGlobalStyles = (filePath, anchor, insert) => {
89
+ return (tree) => {
90
+ const recorder = tree.beginUpdate(filePath);
91
+ const content = tree.readText(filePath);
92
+ let pos = content.indexOf(anchor) + anchor.length;
93
+ if (pos > 0) {
94
+ // If the insert position is not the file start we want to insert the next line after the new line
95
+ pos = content.indexOf('\n', pos) + 1;
96
+ }
97
+ recorder.insertRight(pos, insert);
98
+ tree.commitUpdate(recorder);
99
+ return tree;
100
+ };
101
+ };
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema",
3
+ "$id": "ScssImportToSiemensMigrationSchema",
4
+ "title": "SCSS Import to Siemens Migration Schema",
5
+ "type": "object",
6
+ "properties": {
7
+ "path": {
8
+ "type": "string",
9
+ "description": "Path to the directory where all SCSS imports should be migrated.",
10
+ "x-prompt": "Which directory do you want to migrate?",
11
+ "format": "path",
12
+ "default": "/"
13
+ }
14
+ },
15
+ "required": []
16
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Copyright (c) Siemens 2016 - 2025
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ export const STYLE_REPLACEMENTS = [
6
+ {
7
+ replace: `@import '@simpl/element-theme/`,
8
+ new: `@import '@siemens/element-theme/`
9
+ },
10
+ {
11
+ replace: `@use '@simpl/element-theme/`,
12
+ new: `@use '@siemens/element-theme/`
13
+ }
14
+ ];
15
+ // Apply theme styles if not already present
16
+ export const THEME_STYLE_ENTRIES = [
17
+ { insert: `@use '@simpl/brand/assets/fonts/styles/siemens-sans';` },
18
+ { insert: `@use '@simpl/element-icons/dist/style/simpl-element-icons';` },
19
+ {
20
+ insert: `@use '@siemens/element-theme/src/theme' with (
21
+ $element-theme-default: 'siemens-brand',
22
+ $element-themes: (
23
+ 'siemens-brand',
24
+ 'element'
25
+ )
26
+ );`,
27
+ pattern: /@use '@siemens\/element-theme\/src\/theme' with \(([\s\S]*?)\);/g
28
+ },
29
+ { insert: `@use '@siemens/element-ng/element-ng';` },
30
+ { insert: `@use '@siemens/element-theme/src/styles/themes';` },
31
+ { insert: `@use '@simpl/brand/dist/element-theme-siemens-brand-light' as brand-light;` },
32
+ { insert: `@use '@simpl/brand/dist/element-theme-siemens-brand-dark' as brand-dark;` },
33
+ {
34
+ insert: `@include themes.make-theme(brand-light.$tokens, 'siemens-brand');`,
35
+ pattern: /@include themes\.make-theme\(brand-light\.\$tokens, 'siemens-brand'\);/g
36
+ },
37
+ {
38
+ insert: `@include themes.make-theme(brand-dark.$tokens, 'siemens-brand', true);`,
39
+ pattern: /@include themes\.make-theme\(brand-dark\.\$tokens, 'siemens-brand', true\);/g
40
+ }
41
+ ];
42
+ export const SCSS_USE_PATTERNS = [
43
+ /@use '@simpl\/element-theme\/src\/theme' with \(([\s\S]*?)\);/g,
44
+ /@use '@simpl\/element-ng\/simpl-element-ng' with \(([\s\S]*?)\);/g,
45
+ /@use '@simpl\/element-theme\/src\/theme';/g
46
+ ];
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Copyright (c) Siemens 2016 - 2025
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ import { chain, schematic } from '@angular-devkit/schematics';
6
+ // You don't have to export the function as default. You can also have more than one rule factory
7
+ // per file.
8
+ export const simplSiemensMigration = (_options) => {
9
+ return (tree, context) => {
10
+ context.logger.info('🚀 Starting Simpl to Siemens migration...');
11
+ const chainedRules = chain([
12
+ schematic('migrate-ts-imports-to-siemens', _options),
13
+ schematic('migrate-scss-imports-to-siemens', _options),
14
+ schematic('migrate-v47-to-v48', _options)
15
+ ]);
16
+ return chainedRules(tree, context);
17
+ };
18
+ };
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema",
3
+ "$id": "SimplToSiemensMigration",
4
+ "title": "Simpl to Siemens Migration Schema",
5
+ "type": "object",
6
+ "properties": {
7
+ "path": {
8
+ "type": "string",
9
+ "description": "Path to the directory where all simpl imports should be migrated.",
10
+ "x-prompt": "Which directory do you want to migrate?",
11
+ "format": "path",
12
+ "default": "/"
13
+ }
14
+ },
15
+ "required": []
16
+ }