@siemens/element-ng 51.0.0-rc.8 → 51.0.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.
- package/fesm2022/siemens-element-ng-ag-grid.mjs +17 -25
- package/fesm2022/siemens-element-ng-ag-grid.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-application-header.mjs +2 -2
- package/fesm2022/siemens-element-ng-application-header.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-avatar.mjs +3 -3
- package/fesm2022/siemens-element-ng-avatar.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-chat-messages.mjs +123 -23
- package/fesm2022/siemens-element-ng-chat-messages.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-color-picker.mjs +18 -18
- package/fesm2022/siemens-element-ng-color-picker.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-column-selection-dialog.mjs +2 -2
- package/fesm2022/siemens-element-ng-column-selection-dialog.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-dashboard.mjs +4 -4
- package/fesm2022/siemens-element-ng-dashboard.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-date-range-filter.mjs +2 -2
- package/fesm2022/siemens-element-ng-date-range-filter.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-datepicker.mjs +4 -4
- package/fesm2022/siemens-element-ng-datepicker.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-file-uploader.mjs +2 -2
- package/fesm2022/siemens-element-ng-file-uploader.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-filtered-search.mjs +2 -2
- package/fesm2022/siemens-element-ng-filtered-search.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-form.mjs +19 -6
- package/fesm2022/siemens-element-ng-form.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-formly.mjs +4 -4
- package/fesm2022/siemens-element-ng-formly.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-icon.mjs +1 -1
- package/fesm2022/siemens-element-ng-icon.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-info-page.mjs +2 -2
- package/fesm2022/siemens-element-ng-info-page.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-landing-page.mjs +4 -4
- package/fesm2022/siemens-element-ng-landing-page.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-list-details.mjs +62 -16
- package/fesm2022/siemens-element-ng-list-details.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-main-detail-container.mjs +72 -24
- package/fesm2022/siemens-element-ng-main-detail-container.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-maplibre.mjs +282 -6
- package/fesm2022/siemens-element-ng-maplibre.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-markdown-hightlighter-highlightjs.mjs +2 -2
- package/fesm2022/siemens-element-ng-markdown-hightlighter-highlightjs.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-menu.mjs +6 -6
- package/fesm2022/siemens-element-ng-menu.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-navbar-vertical-next.mjs +6 -6
- package/fesm2022/siemens-element-ng-navbar-vertical-next.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-navbar-vertical.mjs +4 -4
- package/fesm2022/siemens-element-ng-navbar-vertical.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-result-details-list.mjs +2 -2
- package/fesm2022/siemens-element-ng-result-details-list.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-select.mjs +44 -5
- package/fesm2022/siemens-element-ng-select.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-side-panel.mjs +48 -13
- package/fesm2022/siemens-element-ng-side-panel.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-slider.mjs +2 -2
- package/fesm2022/siemens-element-ng-slider.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-source-chip.mjs +2 -2
- package/fesm2022/siemens-element-ng-source-chip.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-split.mjs +18 -16
- package/fesm2022/siemens-element-ng-split.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-status-bar.mjs +2 -2
- package/fesm2022/siemens-element-ng-status-bar.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-status-toggle.mjs +2 -2
- package/fesm2022/siemens-element-ng-status-toggle.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-summary-chip.mjs +2 -2
- package/fesm2022/siemens-element-ng-summary-chip.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-summary-widget.mjs +2 -2
- package/fesm2022/siemens-element-ng-summary-widget.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-system-banner.mjs +5 -5
- package/fesm2022/siemens-element-ng-system-banner.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-tabs.mjs +93 -71
- package/fesm2022/siemens-element-ng-tabs.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-toast-notification.mjs +2 -2
- package/fesm2022/siemens-element-ng-toast-notification.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-tour.mjs +2 -2
- package/fesm2022/siemens-element-ng-tour.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-translate.mjs.map +1 -1
- package/fesm2022/siemens-element-ng-tree-view.mjs +4 -4
- package/fesm2022/siemens-element-ng-tree-view.mjs.map +1 -1
- package/package.json +4 -4
- package/schematics/collection.json +5 -0
- package/schematics/migrate-tabs-legacy/index.js +5 -0
- package/schematics/migrate-tabs-legacy/migrate-tabs-legacy-rule.js +675 -0
- package/schematics/migrate-tabs-legacy/schema.json +14 -0
- package/schematics/migrations/data/css-custom-properties.js +67 -1
- package/schematics/migrations/data/element-class-changes.js +31 -12
- package/schematics/ng-update/migrate-list-details-units.js +86 -0
- package/schematics/ng-update/migrate-main-detail-units.js +89 -0
- package/schematics/ng-update/migrate-split-collapse.js +1 -32
- package/schematics/ng-update/migrate-split-scale.js +524 -0
- package/schematics/ng-update/migrate-split-sizes.js +5 -3
- package/schematics/ng-update/migrate-to-v51.js +6 -0
- package/schematics/utils/index.js +1 -0
- package/schematics/utils/template-edit-utils.js +35 -0
- package/schematics/utils/template-utils.js +31 -45
- package/template-i18n.json +1 -0
- package/types/siemens-element-ng-avatar.d.ts +1 -1
- package/types/siemens-element-ng-chat-messages.d.ts +81 -17
- package/types/siemens-element-ng-form.d.ts +3 -0
- package/types/siemens-element-ng-list-details.d.ts +25 -3
- package/types/siemens-element-ng-main-detail-container.d.ts +23 -3
- package/types/siemens-element-ng-maplibre.d.ts +131 -7
- package/types/siemens-element-ng-select.d.ts +31 -4
- package/types/siemens-element-ng-side-panel.d.ts +17 -3
- package/types/siemens-element-ng-split.d.ts +3 -10
- package/types/siemens-element-ng-system-banner.d.ts +2 -2
- package/types/siemens-element-ng-translate.d.ts +1 -0
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
import { Element } from '@angular/compiler';
|
|
6
|
+
import { dirname, join } from 'path/posix';
|
|
7
|
+
import ts from 'typescript';
|
|
8
|
+
import { removeImportSpecifiers } from '../migrations/utilities/import-removal.js';
|
|
9
|
+
import { discoverSourceFiles, findElement, formatBoundAttribute, getComponentTemplates, getImportSpecifiers, removeAttribute, replaceAttribute } from '../utils/index.js';
|
|
10
|
+
const splitImportPath = /^@(siemens|simpl)\/element-ng\/split(?:\/index)?$/;
|
|
11
|
+
const scaleAttributeNames = ['scale', '[scale]', 'bind-scale'];
|
|
12
|
+
const unitAttributeNames = ['unit', '[unit]', 'bind-unit'];
|
|
13
|
+
export const splitScaleMigrationRule = (options) => {
|
|
14
|
+
return async (tree, context) => {
|
|
15
|
+
const externalTemplates = new Map();
|
|
16
|
+
const manualSizeMigrationPaths = new Set();
|
|
17
|
+
const conflictingUnitPaths = new Set();
|
|
18
|
+
for await (const discoveredSourceFile of discoverSourceFiles(tree, context, options.path)) {
|
|
19
|
+
const { path: filePath, sourceFile } = discoveredSourceFile;
|
|
20
|
+
const scaleImports = getScaleImports(sourceFile);
|
|
21
|
+
const scaleTypeNames = getScaleTypeNames(sourceFile, scaleImports);
|
|
22
|
+
const recorder = tree.beginUpdate(filePath);
|
|
23
|
+
migrateScaleTypes(sourceFile, recorder, scaleImports, scaleTypeNames, discoveredSourceFile.typeChecker);
|
|
24
|
+
for (const template of getComponentTemplates(sourceFile)) {
|
|
25
|
+
const scaleMemberNames = collectScaleMemberNames(template.component, scaleTypeNames);
|
|
26
|
+
if (template.kind === 'inline') {
|
|
27
|
+
const result = migrateScaleTemplate(sourceFile.text.substring(template.node.getStart() + 1, template.node.getEnd() - 1), template.node.getStart() + 1, recorder, [scaleMemberNames]);
|
|
28
|
+
if (result.requiresManualSizeMigration) {
|
|
29
|
+
manualSizeMigrationPaths.add(filePath);
|
|
30
|
+
}
|
|
31
|
+
if (result.hasConflictingUnit) {
|
|
32
|
+
conflictingUnitPaths.add(filePath);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const templatePath = join(dirname(filePath), template.url);
|
|
37
|
+
const owners = externalTemplates.get(templatePath) ?? [];
|
|
38
|
+
owners.push(scaleMemberNames);
|
|
39
|
+
externalTemplates.set(templatePath, owners);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
tree.commitUpdate(recorder);
|
|
43
|
+
}
|
|
44
|
+
// A shared template must be migrated using all of its component contexts.
|
|
45
|
+
for (const [templatePath, owners] of externalTemplates) {
|
|
46
|
+
if (!tree.exists(templatePath)) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const recorder = tree.beginUpdate(templatePath);
|
|
50
|
+
const result = migrateScaleTemplate(tree.readText(templatePath), 0, recorder, owners);
|
|
51
|
+
if (result.requiresManualSizeMigration) {
|
|
52
|
+
manualSizeMigrationPaths.add(templatePath);
|
|
53
|
+
}
|
|
54
|
+
if (result.hasConflictingUnit) {
|
|
55
|
+
conflictingUnitPaths.add(templatePath);
|
|
56
|
+
}
|
|
57
|
+
tree.commitUpdate(recorder);
|
|
58
|
+
}
|
|
59
|
+
if (manualSizeMigrationPaths.size) {
|
|
60
|
+
context.logger.warn(`The following files contain si-split-part elements with scale="none" whose relative size was supplied by [sizes]. Their relative size is preserved with unit="fr" because a pixel size cannot be inferred. Set size and unit="px" manually:\n${[
|
|
61
|
+
...manualSizeMigrationPaths
|
|
62
|
+
]
|
|
63
|
+
.sort()
|
|
64
|
+
.map(path => `- ${path}`)
|
|
65
|
+
.join('\n')}`);
|
|
66
|
+
}
|
|
67
|
+
if (conflictingUnitPaths.size) {
|
|
68
|
+
context.logger.warn(`The following files contain si-split-part elements with conflicting scale and unit values. The existing unit was preserved and scale removed. Review these parts manually:\n${[
|
|
69
|
+
...conflictingUnitPaths
|
|
70
|
+
]
|
|
71
|
+
.sort()
|
|
72
|
+
.map(path => `- ${path}`)
|
|
73
|
+
.join('\n')}`);
|
|
74
|
+
}
|
|
75
|
+
return tree;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
const migrateScaleTemplate = (template, offset, recorder, owners) => {
|
|
79
|
+
const elements = findElement(template, element => element.name === 'si-split' || element.name === 'si-split-part');
|
|
80
|
+
const partsWithRelativeSizes = new Set();
|
|
81
|
+
for (const split of elements) {
|
|
82
|
+
if (split.name !== 'si-split' || !split.attrs.some(attribute => attribute.name === '[sizes]')) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
for (const child of split.children) {
|
|
86
|
+
if (child instanceof Element &&
|
|
87
|
+
child.name === 'si-split-part' &&
|
|
88
|
+
!child.attrs.some(attribute => attribute.name === 'size' || attribute.name === '[size]')) {
|
|
89
|
+
partsWithRelativeSizes.add(child);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
let requiresManualSizeMigration = false;
|
|
94
|
+
let hasConflictingUnit = false;
|
|
95
|
+
elements.forEach(element => {
|
|
96
|
+
if (element.name !== 'si-split-part') {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const scale = element.attrs.find(attribute => scaleAttributeNames.includes(attribute.name));
|
|
100
|
+
if (!scale) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const unit = element.attrs.find(attribute => unitAttributeNames.includes(attribute.name));
|
|
104
|
+
if (unit) {
|
|
105
|
+
hasConflictingUnit ||= hasConflictingStaticUnit(scale, unit);
|
|
106
|
+
removeAttribute(template, scale, offset, recorder);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
let replacement;
|
|
110
|
+
if (partsWithRelativeSizes.has(element) &&
|
|
111
|
+
scale.name === 'scale' &&
|
|
112
|
+
scale.value.trim() === 'none') {
|
|
113
|
+
replacement = 'unit="fr"';
|
|
114
|
+
requiresManualSizeMigration = true;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const expression = scale.name === 'scale' ? getInterpolationExpression(scale.value) : scale.value;
|
|
118
|
+
const replacements = new Set(owners.map(names => getUnitAttribute(scale, names, expression)));
|
|
119
|
+
replacement = replacements.values().next().value;
|
|
120
|
+
if (replacements.size > 1 && expression !== undefined) {
|
|
121
|
+
replacement = formatBoundAttribute('unit', `['none', 'px'].includes(${expression}) ? 'px' : 'fr'`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
replaceAttribute(scale, replacement, offset, recorder);
|
|
125
|
+
});
|
|
126
|
+
return { requiresManualSizeMigration, hasConflictingUnit };
|
|
127
|
+
};
|
|
128
|
+
const hasConflictingStaticUnit = (scale, unit) => {
|
|
129
|
+
const scaleValue = scale.value.trim();
|
|
130
|
+
const unitValue = unit.value.trim();
|
|
131
|
+
return (scale.name === 'scale' &&
|
|
132
|
+
unit.name === 'unit' &&
|
|
133
|
+
(scaleValue === 'auto' || scaleValue === 'none') &&
|
|
134
|
+
(unitValue === 'fr' || unitValue === 'px') &&
|
|
135
|
+
getScaleUnit(scaleValue) !== unitValue);
|
|
136
|
+
};
|
|
137
|
+
const getInterpolationExpression = (value) => /^\s*{{([\s\S]*)}}\s*$/.exec(value)?.[1]?.trim();
|
|
138
|
+
const getUnitAttribute = (attribute, scaleMemberNames, expression) => {
|
|
139
|
+
if (attribute.name === 'scale') {
|
|
140
|
+
if (expression !== undefined) {
|
|
141
|
+
return formatUnitBinding(expression, scaleMemberNames, getExpression(expression));
|
|
142
|
+
}
|
|
143
|
+
return `unit="${getScaleUnit(attribute.value)}"`;
|
|
144
|
+
}
|
|
145
|
+
const parsedExpression = getExpression(attribute.value);
|
|
146
|
+
if (parsedExpression && ts.isStringLiteral(parsedExpression)) {
|
|
147
|
+
return `unit="${getScaleUnit(parsedExpression.text)}"`;
|
|
148
|
+
}
|
|
149
|
+
return formatUnitBinding(attribute.value, scaleMemberNames, parsedExpression);
|
|
150
|
+
};
|
|
151
|
+
const getScaleUnit = (value) => (value.trim() === 'none' ? 'px' : 'fr');
|
|
152
|
+
const getUnitExpression = (expression) => {
|
|
153
|
+
const value = expression.trim();
|
|
154
|
+
return `['none', 'px'].includes(${value}) ? 'px' : 'fr'`;
|
|
155
|
+
};
|
|
156
|
+
const formatUnitBinding = (expression, scaleMemberNames, parsedExpression) => {
|
|
157
|
+
const typedExpression = getTypedScaleExpression(parsedExpression, scaleMemberNames);
|
|
158
|
+
const mappedExpression = getMappedScaleExpression(expression, parsedExpression);
|
|
159
|
+
const value = typedExpression ?? mappedExpression ?? getUnitExpression(expression);
|
|
160
|
+
return formatBoundAttribute('unit', value);
|
|
161
|
+
};
|
|
162
|
+
const getScaleImports = (sourceFile) => {
|
|
163
|
+
const importedSpecifiers = new Set(getImportSpecifiers(sourceFile, splitImportPath, 'Scale'));
|
|
164
|
+
return sourceFile.statements.flatMap(statement => {
|
|
165
|
+
if (!ts.isImportDeclaration(statement)) {
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
const namedBindings = statement.importClause?.namedBindings;
|
|
169
|
+
if (!namedBindings || !ts.isNamedImports(namedBindings)) {
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
return namedBindings.elements
|
|
173
|
+
.filter(element => importedSpecifiers.has(element))
|
|
174
|
+
.map(element => ({ declaration: statement, element }));
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
const getScaleTypeNames = (sourceFile, scaleImports) => {
|
|
178
|
+
const names = new Set(scaleImports.map(({ element }) => element.name.text));
|
|
179
|
+
collectScaleTypeAliases(sourceFile, names);
|
|
180
|
+
return names;
|
|
181
|
+
};
|
|
182
|
+
const collectScaleMemberNames = (component, scaleTypeNames) => {
|
|
183
|
+
const names = new Set();
|
|
184
|
+
for (const node of component.members) {
|
|
185
|
+
if (ts.canHaveModifiers(node) &&
|
|
186
|
+
ts.getModifiers(node)?.some(modifier => modifier.kind === ts.SyntaxKind.StaticKeyword)) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (ts.isPropertyDeclaration(node) &&
|
|
190
|
+
ts.isIdentifier(node.name) &&
|
|
191
|
+
(isScaleType(node.type, scaleTypeNames) ||
|
|
192
|
+
isScaleInitializer(node.initializer, scaleTypeNames))) {
|
|
193
|
+
names.add(node.name.text);
|
|
194
|
+
}
|
|
195
|
+
if ((ts.isMethodDeclaration(node) || ts.isGetAccessorDeclaration(node)) &&
|
|
196
|
+
node.name &&
|
|
197
|
+
ts.isIdentifier(node.name) &&
|
|
198
|
+
isScaleType(node.type, scaleTypeNames)) {
|
|
199
|
+
names.add(node.name.text);
|
|
200
|
+
}
|
|
201
|
+
if (ts.isPropertyDeclaration(node) &&
|
|
202
|
+
ts.isIdentifier(node.name) &&
|
|
203
|
+
node.initializer &&
|
|
204
|
+
(ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer)) &&
|
|
205
|
+
isScaleType(node.initializer.type, scaleTypeNames)) {
|
|
206
|
+
names.add(node.name.text);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return names;
|
|
210
|
+
};
|
|
211
|
+
const isScaleInitializer = (initializer, scaleTypeNames) => {
|
|
212
|
+
if (!initializer || !ts.isCallExpression(initializer)) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
return (initializer.typeArguments?.some(typeArgument => isScaleType(typeArgument, scaleTypeNames)) ===
|
|
216
|
+
true);
|
|
217
|
+
};
|
|
218
|
+
const migrateScaleTypes = (sourceFile, recorder, scaleImports, scaleTypeNames, typeChecker) => {
|
|
219
|
+
if (!scaleImports.length) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const replacements = new Map();
|
|
223
|
+
const importEdits = [];
|
|
224
|
+
const existingUnit = getImportSpecifiers(sourceFile, splitImportPath, 'SplitUnit')[0];
|
|
225
|
+
for (const importDeclaration of new Set(scaleImports.map(({ declaration }) => declaration))) {
|
|
226
|
+
const imports = scaleImports.filter(item => item.declaration === importDeclaration);
|
|
227
|
+
if (existingUnit) {
|
|
228
|
+
imports.forEach(({ element }) => replacements.set(element.name.text, existingUnit.name.text));
|
|
229
|
+
const edit = removeImportSpecifiers(sourceFile, importDeclaration, imports.map(({ element }) => element));
|
|
230
|
+
importEdits.push({
|
|
231
|
+
start: edit.start,
|
|
232
|
+
width: edit.width,
|
|
233
|
+
replacement: edit.newNode
|
|
234
|
+
? ts.createPrinter().printNode(ts.EmitHint.Unspecified, edit.newNode, sourceFile)
|
|
235
|
+
: undefined
|
|
236
|
+
});
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
for (const { element } of imports) {
|
|
240
|
+
if (element.propertyName) {
|
|
241
|
+
importEdits.push({
|
|
242
|
+
start: element.propertyName.getStart(sourceFile),
|
|
243
|
+
width: element.propertyName.getWidth(sourceFile),
|
|
244
|
+
replacement: 'SplitUnit'
|
|
245
|
+
});
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
importEdits.push({
|
|
249
|
+
start: element.name.getStart(sourceFile),
|
|
250
|
+
width: element.name.getWidth(sourceFile),
|
|
251
|
+
replacement: 'SplitUnit'
|
|
252
|
+
});
|
|
253
|
+
replacements.set(element.name.text, 'SplitUnit');
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
for (const edit of importEdits) {
|
|
257
|
+
recorder.remove(edit.start, edit.width);
|
|
258
|
+
if (edit.replacement) {
|
|
259
|
+
recorder.insertLeft(edit.start, edit.replacement);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
replaceScaleIdentifiers(sourceFile, replacements, recorder);
|
|
263
|
+
replaceScaleLiterals(sourceFile, scaleTypeNames, typeChecker, recorder);
|
|
264
|
+
};
|
|
265
|
+
const collectScaleTypeAliases = (sourceFile, names) => {
|
|
266
|
+
let changed = true;
|
|
267
|
+
while (changed) {
|
|
268
|
+
changed = false;
|
|
269
|
+
for (const statement of sourceFile.statements) {
|
|
270
|
+
if (ts.isTypeAliasDeclaration(statement) &&
|
|
271
|
+
isScaleType(statement.type, names) &&
|
|
272
|
+
!names.has(statement.name.text)) {
|
|
273
|
+
names.add(statement.name.text);
|
|
274
|
+
changed = true;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
const isScaleType = (type, names) => {
|
|
280
|
+
if (!type) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
if (ts.isTypeReferenceNode(type)) {
|
|
284
|
+
return ts.isIdentifier(type.typeName) && names.has(type.typeName.text);
|
|
285
|
+
}
|
|
286
|
+
if (ts.isParenthesizedTypeNode(type)) {
|
|
287
|
+
return isScaleType(type.type, names);
|
|
288
|
+
}
|
|
289
|
+
if (ts.isUnionTypeNode(type) || ts.isIntersectionTypeNode(type)) {
|
|
290
|
+
return type.types.some(member => isScaleType(member, names));
|
|
291
|
+
}
|
|
292
|
+
return false;
|
|
293
|
+
};
|
|
294
|
+
const replaceScaleIdentifiers = (sourceFile, replacements, recorder) => {
|
|
295
|
+
if (!replacements.size) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const visit = (node) => {
|
|
299
|
+
if (ts.isImportDeclaration(node)) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (ts.isIdentifier(node) && isScaleTypeReference(node)) {
|
|
303
|
+
const replacement = replacements.get(node.text);
|
|
304
|
+
if (replacement) {
|
|
305
|
+
recorder.remove(node.getStart(sourceFile), node.getWidth(sourceFile));
|
|
306
|
+
recorder.insertLeft(node.getStart(sourceFile), replacement);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
ts.forEachChild(node, visit);
|
|
311
|
+
};
|
|
312
|
+
sourceFile.statements.forEach(statement => visit(statement));
|
|
313
|
+
};
|
|
314
|
+
const isScaleTypeReference = (node) => {
|
|
315
|
+
const parent = node.parent;
|
|
316
|
+
if (ts.isTypeReferenceNode(parent)) {
|
|
317
|
+
return parent.typeName === node;
|
|
318
|
+
}
|
|
319
|
+
if (ts.isExpressionWithTypeArguments(parent)) {
|
|
320
|
+
return parent.expression === node;
|
|
321
|
+
}
|
|
322
|
+
if (ts.isTypeQueryNode(parent)) {
|
|
323
|
+
return parent.exprName === node;
|
|
324
|
+
}
|
|
325
|
+
if (ts.isExportSpecifier(parent)) {
|
|
326
|
+
return !parent.propertyName || parent.propertyName === node;
|
|
327
|
+
}
|
|
328
|
+
return false;
|
|
329
|
+
};
|
|
330
|
+
const replaceScaleLiterals = (sourceFile, scaleTypeNames, typeChecker, recorder) => {
|
|
331
|
+
const typedNames = new Set();
|
|
332
|
+
const literals = new Set();
|
|
333
|
+
const collectLiterals = (node) => {
|
|
334
|
+
if (!node) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (ts.isStringLiteral(node) && isScaleLiteral(node)) {
|
|
338
|
+
literals.add(node);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (ts.isParenthesizedExpression(node)) {
|
|
342
|
+
collectLiterals(node.expression);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (ts.isAsExpression(node) ||
|
|
346
|
+
ts.isTypeAssertionExpression(node) ||
|
|
347
|
+
ts.isNonNullExpression(node)) {
|
|
348
|
+
collectLiterals(node.expression);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (ts.isConditionalExpression(node)) {
|
|
352
|
+
collectLiterals(node.whenTrue);
|
|
353
|
+
collectLiterals(node.whenFalse);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
|
|
357
|
+
collectLiterals(node.body);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
361
|
+
node.properties.forEach(property => {
|
|
362
|
+
if (!ts.isPropertyAssignment(property)) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const contextualType = typeChecker.getContextualType(property.initializer);
|
|
366
|
+
if (isScaleCheckerType(contextualType, scaleTypeNames)) {
|
|
367
|
+
collectLiterals(property.initializer);
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
const collectContextualLiterals = (node) => {
|
|
373
|
+
if (ts.isStringLiteral(node) &&
|
|
374
|
+
isScaleLiteral(node) &&
|
|
375
|
+
isScaleCheckerType(typeChecker.getContextualType(node), scaleTypeNames)) {
|
|
376
|
+
literals.add(node);
|
|
377
|
+
}
|
|
378
|
+
ts.forEachChild(node, collectContextualLiterals);
|
|
379
|
+
};
|
|
380
|
+
const visit = (node) => {
|
|
381
|
+
if ((ts.isVariableDeclaration(node) || ts.isPropertyDeclaration(node)) &&
|
|
382
|
+
ts.isIdentifier(node.name)) {
|
|
383
|
+
const hasScaleType = isScaleType(node.type, scaleTypeNames);
|
|
384
|
+
const scaleInitializer = node.initializer && ts.isCallExpression(node.initializer) ? node.initializer : undefined;
|
|
385
|
+
const hasScaleInitializer = !!scaleInitializer && isScaleInitializer(scaleInitializer, scaleTypeNames);
|
|
386
|
+
if (hasScaleType || hasScaleInitializer) {
|
|
387
|
+
typedNames.add(node.name.text);
|
|
388
|
+
if (hasScaleInitializer) {
|
|
389
|
+
collectLiterals(scaleInitializer.arguments[0]);
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
collectLiterals(node.initializer);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (ts.isParameter(node) && ts.isIdentifier(node.name)) {
|
|
397
|
+
if (isScaleType(node.type, scaleTypeNames)) {
|
|
398
|
+
typedNames.add(node.name.text);
|
|
399
|
+
collectLiterals(node.initializer);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const isFunctionWithBody = ts.isFunctionDeclaration(node) ||
|
|
403
|
+
ts.isMethodDeclaration(node) ||
|
|
404
|
+
ts.isArrowFunction(node) ||
|
|
405
|
+
ts.isFunctionExpression(node) ||
|
|
406
|
+
ts.isGetAccessorDeclaration(node) ||
|
|
407
|
+
ts.isSetAccessorDeclaration(node);
|
|
408
|
+
if (isFunctionWithBody && isScaleType(node.type, scaleTypeNames)) {
|
|
409
|
+
if (node.body) {
|
|
410
|
+
const collectReturns = (child) => {
|
|
411
|
+
if (ts.isReturnStatement(child) && child.expression) {
|
|
412
|
+
collectLiterals(child.expression);
|
|
413
|
+
}
|
|
414
|
+
ts.forEachChild(child, collectReturns);
|
|
415
|
+
};
|
|
416
|
+
collectReturns(node.body);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
420
|
+
const root = getRootIdentifier(node.left);
|
|
421
|
+
if (root && typedNames.has(root)) {
|
|
422
|
+
collectLiterals(node.right);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (ts.isCallExpression(node) &&
|
|
426
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
427
|
+
(node.expression.name.text === 'set' || node.expression.name.text === 'update') &&
|
|
428
|
+
typedNames.has(getRootIdentifier(node.expression.expression) ?? '')) {
|
|
429
|
+
node.arguments.forEach(collectLiterals);
|
|
430
|
+
}
|
|
431
|
+
ts.forEachChild(node, visit);
|
|
432
|
+
};
|
|
433
|
+
sourceFile.statements.filter(statement => !ts.isImportDeclaration(statement)).forEach(visit);
|
|
434
|
+
sourceFile.statements
|
|
435
|
+
.filter(statement => !ts.isImportDeclaration(statement))
|
|
436
|
+
.forEach(collectContextualLiterals);
|
|
437
|
+
literals.forEach(literal => {
|
|
438
|
+
const replacement = literal.text === 'auto' ? 'fr' : 'px';
|
|
439
|
+
const start = literal.getStart(sourceFile) + 1;
|
|
440
|
+
recorder.remove(start, literal.getWidth(sourceFile) - 2);
|
|
441
|
+
recorder.insertLeft(start, replacement);
|
|
442
|
+
});
|
|
443
|
+
};
|
|
444
|
+
const isScaleLiteral = (node) => node.text === 'auto' || node.text === 'none';
|
|
445
|
+
const isScaleCheckerType = (type, names) => {
|
|
446
|
+
if (!type) {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
if (type.isUnionOrIntersection()) {
|
|
450
|
+
return type.types.some(member => isScaleCheckerType(member, names));
|
|
451
|
+
}
|
|
452
|
+
return names.has(type.aliasSymbol?.name ?? '') || names.has(type.symbol?.name ?? '');
|
|
453
|
+
};
|
|
454
|
+
const getTypedScaleExpression = (expression, scaleMemberNames) => {
|
|
455
|
+
return expression && isTypedScaleExpression(expression, scaleMemberNames)
|
|
456
|
+
? expression.getText()
|
|
457
|
+
: undefined;
|
|
458
|
+
};
|
|
459
|
+
const isTypedScaleExpression = (expression, scaleMemberNames) => {
|
|
460
|
+
if (ts.isIdentifier(expression)) {
|
|
461
|
+
return scaleMemberNames.has(expression.text);
|
|
462
|
+
}
|
|
463
|
+
if (ts.isPropertyAccessExpression(expression)) {
|
|
464
|
+
return (expression.expression.kind === ts.SyntaxKind.ThisKeyword &&
|
|
465
|
+
scaleMemberNames.has(expression.name.text));
|
|
466
|
+
}
|
|
467
|
+
if (ts.isCallExpression(expression)) {
|
|
468
|
+
return isTypedScaleExpression(expression.expression, scaleMemberNames);
|
|
469
|
+
}
|
|
470
|
+
if (ts.isParenthesizedExpression(expression)) {
|
|
471
|
+
return isTypedScaleExpression(expression.expression, scaleMemberNames);
|
|
472
|
+
}
|
|
473
|
+
return false;
|
|
474
|
+
};
|
|
475
|
+
const getMappedScaleExpression = (expression, initializer) => {
|
|
476
|
+
if (!initializer || !ts.isConditionalExpression(initializer)) {
|
|
477
|
+
return undefined;
|
|
478
|
+
}
|
|
479
|
+
const replacements = [];
|
|
480
|
+
const source = initializer.getSourceFile();
|
|
481
|
+
const collect = (node) => {
|
|
482
|
+
if (ts.isStringLiteral(node) && isScaleLiteral(node)) {
|
|
483
|
+
replacements.push({
|
|
484
|
+
start: node.getStart(source) + 1,
|
|
485
|
+
end: node.getEnd() - 1,
|
|
486
|
+
value: node.text === 'none' ? 'px' : 'fr'
|
|
487
|
+
});
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
if (ts.isConditionalExpression(node)) {
|
|
491
|
+
collect(node.whenTrue);
|
|
492
|
+
collect(node.whenFalse);
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
collect(initializer);
|
|
496
|
+
if (!replacements.length) {
|
|
497
|
+
return undefined;
|
|
498
|
+
}
|
|
499
|
+
const leadingWhitespaceLength = expression.length - expression.trimStart().length;
|
|
500
|
+
const syntheticSourcePrefixLength = initializer.getStart(source) - leadingWhitespaceLength;
|
|
501
|
+
return replacements
|
|
502
|
+
.sort((first, second) => second.start - first.start)
|
|
503
|
+
.reduce((value, replacement) => {
|
|
504
|
+
return (value.slice(0, replacement.start - syntheticSourcePrefixLength) +
|
|
505
|
+
replacement.value +
|
|
506
|
+
value.slice(replacement.end - syntheticSourcePrefixLength));
|
|
507
|
+
}, expression);
|
|
508
|
+
};
|
|
509
|
+
const getRootIdentifier = (node) => {
|
|
510
|
+
if (ts.isIdentifier(node)) {
|
|
511
|
+
return node.text;
|
|
512
|
+
}
|
|
513
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
514
|
+
return node.name.text;
|
|
515
|
+
}
|
|
516
|
+
return undefined;
|
|
517
|
+
};
|
|
518
|
+
const getExpression = (value) => {
|
|
519
|
+
const statement = ts.createSourceFile('split-scale-expression.ts', `const value = ${value};`, ts.ScriptTarget.Latest, true).statements[0];
|
|
520
|
+
if (!statement || !ts.isVariableStatement(statement)) {
|
|
521
|
+
return undefined;
|
|
522
|
+
}
|
|
523
|
+
return statement.declarationList.declarations[0]?.initializer;
|
|
524
|
+
};
|
|
@@ -46,9 +46,11 @@ const migrateSplitSizes = (template, offset, recorder) => {
|
|
|
46
46
|
if (part.attrs.some(attribute => attribute.name === 'size' || attribute.name === '[size]')) {
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
|
+
const hasUnit = parts[index].attrs.some(attribute => ['unit', '[unit]', 'bind-unit'].includes(attribute.name));
|
|
50
|
+
const unitAttribute = hasUnit ? '' : ' unit="fr"';
|
|
49
51
|
const sizeAttribute = literalSizes
|
|
50
|
-
? ` size="${literalSizes[index]}"
|
|
51
|
-
: ` [size]="${getIndexedSizeExpression(sizes.value, index)}"
|
|
52
|
+
? ` size="${literalSizes[index]}"${unitAttribute}`
|
|
53
|
+
: ` [size]="${getIndexedSizeExpression(sizes.value, index)}"${unitAttribute}`;
|
|
52
54
|
const startTag = part.startSourceSpan.toString();
|
|
53
55
|
const insertOffset = part.startSourceSpan.end.offset - (startTag.endsWith('/>') ? 2 : 1);
|
|
54
56
|
recorder.insertLeft(insertOffset + offset, sizeAttribute);
|
|
@@ -94,7 +96,7 @@ const getIndexedSizeExpression = (expression, index) => {
|
|
|
94
96
|
const migrateMissingSplitUnit = (template, offset, recorder) => {
|
|
95
97
|
findElement(template, element => element.name === 'si-split-part').forEach(part => {
|
|
96
98
|
const hasSize = part.attrs.some(a => a.name === 'size' || a.name === '[size]');
|
|
97
|
-
const hasUnit = part.attrs.some(a =>
|
|
99
|
+
const hasUnit = part.attrs.some(a => ['unit', '[unit]', 'bind-unit'].includes(a.name));
|
|
98
100
|
if (!hasSize || hasUnit) {
|
|
99
101
|
return;
|
|
100
102
|
}
|
|
@@ -7,9 +7,12 @@ import { getElementMigrationData } from '../migrations/data/index.js';
|
|
|
7
7
|
import { elementMigrationRule } from '../migrations/element-migration/element-migration.js';
|
|
8
8
|
import { missingTranslateMigrationRule } from '../migrations/ngx-translate/index.js';
|
|
9
9
|
import { contentFormatterMigrationRule } from './migrate-content-formatter.js';
|
|
10
|
+
import { listDetailsUnitsMigrationRule } from './migrate-list-details-units.js';
|
|
11
|
+
import { mainDetailUnitsMigrationRule } from './migrate-main-detail-units.js';
|
|
10
12
|
import { markdownRendererMigrationRule } from './migrate-markdown-renderer.js';
|
|
11
13
|
import { spacerMigrationRule } from './migrate-spacers.js';
|
|
12
14
|
import { splitCollapseMigrationRule } from './migrate-split-collapse.js';
|
|
15
|
+
import { splitScaleMigrationRule } from './migrate-split-scale.js';
|
|
13
16
|
import { splitSizesMigrationRule } from './migrate-split-sizes.js';
|
|
14
17
|
export const migrateToV51 = () => {
|
|
15
18
|
return (tree, context) => {
|
|
@@ -19,8 +22,11 @@ export const migrateToV51 = () => {
|
|
|
19
22
|
return chain([
|
|
20
23
|
elementMigrationRule(options, migrationData),
|
|
21
24
|
missingTranslateMigrationRule(options),
|
|
25
|
+
splitScaleMigrationRule(options),
|
|
22
26
|
splitSizesMigrationRule(options),
|
|
23
27
|
splitCollapseMigrationRule(options),
|
|
28
|
+
mainDetailUnitsMigrationRule(options),
|
|
29
|
+
listDetailsUnitsMigrationRule(options),
|
|
24
30
|
contentFormatterMigrationRule(options),
|
|
25
31
|
markdownRendererMigrationRule(options),
|
|
26
32
|
spacerMigrationRule(options)
|
|
@@ -7,5 +7,6 @@ export * from './project-utils.js';
|
|
|
7
7
|
export * from './ts-compiler-host.js';
|
|
8
8
|
export * from './schematics-file-system.js';
|
|
9
9
|
export * from './template-utils.js';
|
|
10
|
+
export * from './template-edit-utils.js';
|
|
10
11
|
export * from './testing.js';
|
|
11
12
|
export * from './ts-utils.js';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Siemens 2016 - 2026
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
export const formatBoundAttribute = (name, expression) => {
|
|
6
|
+
const quote = expression.includes('"') && !expression.includes("'") ? "'" : '"';
|
|
7
|
+
const escapedExpression = quote === '"' ? expression.replaceAll('"', '"') : expression.replaceAll("'", ''');
|
|
8
|
+
return `[${name}]=${quote}${escapedExpression}${quote}`;
|
|
9
|
+
};
|
|
10
|
+
export const replaceAttribute = (attribute, replacement, offset, recorder) => {
|
|
11
|
+
const start = attribute.sourceSpan.start.offset + offset;
|
|
12
|
+
recorder.remove(start, attribute.sourceSpan.end.offset - attribute.sourceSpan.start.offset);
|
|
13
|
+
recorder.insertLeft(start, replacement);
|
|
14
|
+
};
|
|
15
|
+
export const insertAttribute = (template, element, attribute, offset, recorder) => {
|
|
16
|
+
const selfClosing = element.startSourceSpan.toString().endsWith('/>');
|
|
17
|
+
const insertOffset = element.startSourceSpan.end.offset - (selfClosing ? 2 : 1);
|
|
18
|
+
const prefix = /\s/.test(template[insertOffset - 1] ?? '') ? '' : ' ';
|
|
19
|
+
recorder.insertLeft(insertOffset + offset, `${prefix}${attribute}${selfClosing ? ' ' : ''}`);
|
|
20
|
+
};
|
|
21
|
+
export const removeAttribute = (template, attribute, offset, recorder) => {
|
|
22
|
+
const start = attribute.sourceSpan.start.offset;
|
|
23
|
+
const end = attribute.sourceSpan.end.offset;
|
|
24
|
+
const lineStart = template.lastIndexOf('\n', start - 1) + 1;
|
|
25
|
+
const lineEnd = template.indexOf('\n', end);
|
|
26
|
+
const endOfLine = lineEnd === -1 ? template.length : lineEnd;
|
|
27
|
+
if (template.slice(lineStart, start).trim() === '' &&
|
|
28
|
+
template.slice(end, endOfLine).trim() === '') {
|
|
29
|
+
const removeEnd = lineEnd === -1 ? endOfLine : lineEnd + 1;
|
|
30
|
+
recorder.remove(lineStart + offset, removeEnd - lineStart);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const removeStart = start > 0 && /\s/.test(template[start - 1] ?? '') ? start - 1 : start;
|
|
34
|
+
recorder.remove(removeStart + offset, end - removeStart);
|
|
35
|
+
};
|