@ethlete/cdk 4.70.0 ā 5.0.0-next.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/CHANGELOG.md +36 -0
- package/fesm2022/ethlete-cdk.mjs +11055 -12514
- package/fesm2022/ethlete-cdk.mjs.map +1 -1
- package/generators/generators.json +9 -0
- package/generators/migrate-to-v5/cdk-menu.js +259 -0
- package/generators/migrate-to-v5/color-themes.js +221 -0
- package/generators/migrate-to-v5/combobox.js +186 -0
- package/generators/migrate-to-v5/dialog-bottom-sheet.js +1039 -0
- package/generators/migrate-to-v5/et-let.js +514 -0
- package/generators/migrate-to-v5/is-active-element.js +201 -0
- package/generators/migrate-to-v5/migration.js +53 -0
- package/generators/migrate-to-v5/overlay-positions.js +868 -0
- package/generators/migrate-to-v5/schema.json +49 -0
- package/package.json +20 -14
- package/src/lib/styles/cdk.css +1 -1
- package/{index.d.ts ā types/ethlete-cdk.d.ts} +2248 -3617
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
const SYMBOL_MAP = {
|
|
2
|
+
CdkMenuTrigger: 'MenuTriggerDirective',
|
|
3
|
+
CdkMenuItem: 'MenuItemDirective',
|
|
4
|
+
CdkMenu: 'MenuComponent',
|
|
5
|
+
CdkMenuGroup: 'MenuGroupDirective',
|
|
6
|
+
CdkMenuItemCheckbox: 'MenuCheckboxItemComponent',
|
|
7
|
+
CdkMenuItemRadio: 'MenuRadioItemComponent',
|
|
8
|
+
CdkMenuModule: 'MenuImports',
|
|
9
|
+
};
|
|
10
|
+
const CDK_MENU_SYMBOLS = Object.keys(SYMBOL_MAP);
|
|
11
|
+
// These are the et menu symbols that should be consolidated into MenuImports in decorators
|
|
12
|
+
const ET_MENU_SYMBOLS = new Set([
|
|
13
|
+
'MenuGroupDirective',
|
|
14
|
+
'MenuCheckboxItemComponent',
|
|
15
|
+
'MenuRadioItemComponent',
|
|
16
|
+
'MenuGroupTitleDirective',
|
|
17
|
+
'MenuItemDirective',
|
|
18
|
+
'MenuTriggerDirective',
|
|
19
|
+
'MenuCheckboxGroupDirective',
|
|
20
|
+
'MenuRadioGroupDirective',
|
|
21
|
+
'MenuComponent',
|
|
22
|
+
'MenuSearchTemplateDirective',
|
|
23
|
+
]);
|
|
24
|
+
export default async function migrateCdkMenu(tree) {
|
|
25
|
+
console.log('\nš Migrating from CDK menu to et menu');
|
|
26
|
+
const files = tree.children('.');
|
|
27
|
+
const warnings = new Map();
|
|
28
|
+
function walkFiles(path) {
|
|
29
|
+
const entries = tree.children(path);
|
|
30
|
+
entries.forEach((entry) => {
|
|
31
|
+
const fullPath = `${path}/${entry}`.replace(/^\//, '');
|
|
32
|
+
if (tree.isFile(fullPath)) {
|
|
33
|
+
if (fullPath.endsWith('.ts') && !fullPath.endsWith('.spec.ts')) {
|
|
34
|
+
migrateTypeScriptFile(fullPath);
|
|
35
|
+
}
|
|
36
|
+
else if (fullPath.endsWith('.html')) {
|
|
37
|
+
migrateHtmlFile(fullPath, warnings);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
walkFiles(fullPath);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
walkFiles('.');
|
|
46
|
+
// Print warnings at the end
|
|
47
|
+
if (warnings.size > 0) {
|
|
48
|
+
console.log('\nā ļø Manual migration required for the following files:');
|
|
49
|
+
warnings.forEach((msgs, file) => {
|
|
50
|
+
console.log(`\nš ${file}:`);
|
|
51
|
+
msgs.forEach((msg) => console.log(` - ${msg}`));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function migrateTypeScriptFile(filePath) {
|
|
55
|
+
let content = tree.read(filePath, 'utf-8');
|
|
56
|
+
if (!content)
|
|
57
|
+
return;
|
|
58
|
+
const originalContent = content;
|
|
59
|
+
// Find all @ethlete/cdk imports
|
|
60
|
+
const importRegex = /import\s*{([^}]*)}\s*from\s*['"]@ethlete\/cdk['"]/g;
|
|
61
|
+
const imports = Array.from(content.matchAll(importRegex));
|
|
62
|
+
if (imports.length === 0)
|
|
63
|
+
return;
|
|
64
|
+
// Process imports and build replacement
|
|
65
|
+
let hasMenuSymbols = false;
|
|
66
|
+
let hasMenuImports = false;
|
|
67
|
+
const importedSymbols = new Set();
|
|
68
|
+
imports.forEach((match) => {
|
|
69
|
+
const importList = match[1];
|
|
70
|
+
const symbols = importList
|
|
71
|
+
.split(',')
|
|
72
|
+
.map((s) => s.trim())
|
|
73
|
+
.filter((s) => s);
|
|
74
|
+
symbols.forEach((symbol) => {
|
|
75
|
+
importedSymbols.add(symbol);
|
|
76
|
+
if (CDK_MENU_SYMBOLS.includes(symbol)) {
|
|
77
|
+
hasMenuSymbols = true;
|
|
78
|
+
}
|
|
79
|
+
if (symbol === 'MenuImports') {
|
|
80
|
+
hasMenuImports = true;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
if (!hasMenuSymbols && !Array.from(importedSymbols).some((s) => ET_MENU_SYMBOLS.has(s)))
|
|
85
|
+
return;
|
|
86
|
+
// Handle decorator imports arrays FIRST (before replacing symbols in code)
|
|
87
|
+
const decoratorRegex = /imports\s*:\s*\[([\s\S]*?)\]/g;
|
|
88
|
+
let usesMenuInDecorator = false;
|
|
89
|
+
const menuSymbolsInDecorator = new Set();
|
|
90
|
+
content = content.replace(decoratorRegex, (match) => {
|
|
91
|
+
let hasMenuSymbol = false;
|
|
92
|
+
let result = match;
|
|
93
|
+
// Check for CDK menu symbols
|
|
94
|
+
CDK_MENU_SYMBOLS.forEach((symbol) => {
|
|
95
|
+
if (result.includes(symbol)) {
|
|
96
|
+
hasMenuSymbol = true;
|
|
97
|
+
menuSymbolsInDecorator.add(symbol);
|
|
98
|
+
result = result.replace(new RegExp(`\\b${symbol}\\b`, 'g'), '');
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
// Check for et menu symbols
|
|
102
|
+
ET_MENU_SYMBOLS.forEach((symbol) => {
|
|
103
|
+
if (result.includes(symbol)) {
|
|
104
|
+
hasMenuSymbol = true;
|
|
105
|
+
menuSymbolsInDecorator.add(symbol);
|
|
106
|
+
result = result.replace(new RegExp(`\\b${symbol}\\b`, 'g'), '');
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
if (hasMenuSymbol) {
|
|
110
|
+
usesMenuInDecorator = true;
|
|
111
|
+
// Extract the array content and clean it up
|
|
112
|
+
const arrayMatch = result.match(/imports\s*:\s*\[([\s\S]*?)\]/);
|
|
113
|
+
if (arrayMatch) {
|
|
114
|
+
const arrayContent = arrayMatch[1];
|
|
115
|
+
// Split by comma, trim, filter empty strings
|
|
116
|
+
const items = arrayContent
|
|
117
|
+
.split(',')
|
|
118
|
+
.map((item) => item.trim())
|
|
119
|
+
.filter((item) => item && item !== 'MenuImports');
|
|
120
|
+
// Reconstruct with MenuImports
|
|
121
|
+
if (items.length === 0) {
|
|
122
|
+
result = result.replace(/imports\s*:\s*\[([\s\S]*?)\]/, 'imports: [MenuImports]');
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const newContent = `MenuImports, ${items.join(', ')}`;
|
|
126
|
+
result = result.replace(/imports\s*:\s*\[([\s\S]*?)\]/, `imports: [${newContent}]`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
});
|
|
132
|
+
// Replace imports
|
|
133
|
+
content = content.replace(importRegex, (match, importList) => {
|
|
134
|
+
const symbols = importList
|
|
135
|
+
.split(',')
|
|
136
|
+
.map((s) => s.trim())
|
|
137
|
+
.filter((s) => s);
|
|
138
|
+
const replaced = symbols
|
|
139
|
+
.filter((symbol) => !menuSymbolsInDecorator.has(symbol))
|
|
140
|
+
.map((symbol) => {
|
|
141
|
+
if (CDK_MENU_SYMBOLS.includes(symbol)) {
|
|
142
|
+
return SYMBOL_MAP[symbol];
|
|
143
|
+
}
|
|
144
|
+
return symbol;
|
|
145
|
+
});
|
|
146
|
+
// Add MenuImports if used in decorator and not already present
|
|
147
|
+
if (usesMenuInDecorator && !replaced.includes('MenuImports')) {
|
|
148
|
+
replaced.push('MenuImports');
|
|
149
|
+
}
|
|
150
|
+
// Deduplicate
|
|
151
|
+
const unique = Array.from(new Set(replaced));
|
|
152
|
+
return `import { ${unique.join(', ')} } from '@ethlete/cdk'`;
|
|
153
|
+
});
|
|
154
|
+
// Replace symbol usages in code
|
|
155
|
+
CDK_MENU_SYMBOLS.forEach((oldSymbol) => {
|
|
156
|
+
if (importedSymbols.has(oldSymbol) && !menuSymbolsInDecorator.has(oldSymbol)) {
|
|
157
|
+
const newSymbol = SYMBOL_MAP[oldSymbol];
|
|
158
|
+
content = content.replace(new RegExp(`\\b${oldSymbol}\\b`, 'g'), newSymbol);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
if (content !== originalContent) {
|
|
162
|
+
tree.write(filePath, content);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function migrateHtmlFile(filePath, warnings) {
|
|
166
|
+
let content = tree.read(filePath, 'utf-8');
|
|
167
|
+
if (!content)
|
|
168
|
+
return;
|
|
169
|
+
const originalContent = content;
|
|
170
|
+
const fileWarnings = [];
|
|
171
|
+
// 1. Replace [cdkMenuTriggerFor] binding with [etMenuTrigger]
|
|
172
|
+
if (content.includes('[cdkMenuTriggerFor]')) {
|
|
173
|
+
content = content.replace(/\[cdkMenuTriggerFor\]/g, '[etMenuTrigger]');
|
|
174
|
+
}
|
|
175
|
+
const cdkMenuRegex = /<(\w+)([^>]*?)\bcdkMenu\b([^>]*)>/g;
|
|
176
|
+
const matches = Array.from(content.matchAll(cdkMenuRegex));
|
|
177
|
+
if (matches.length > 0) {
|
|
178
|
+
fileWarnings.push(`Found ${matches.length} element(s) with cdkMenu directive. ` +
|
|
179
|
+
'Replaced with <et-menu> component. Verify all attributes and bindings are preserved.');
|
|
180
|
+
// Collect replacements for entire elements
|
|
181
|
+
const replacements = [];
|
|
182
|
+
for (const match of matches) {
|
|
183
|
+
const tagName = match[1];
|
|
184
|
+
const beforeAttrs = match[2];
|
|
185
|
+
const afterAttrs = match[3];
|
|
186
|
+
const allAttrs = (beforeAttrs + afterAttrs)
|
|
187
|
+
.replace(/\s+cdkMenu\s+/, ' ')
|
|
188
|
+
.replace(/\bcdkMenu\b/, '')
|
|
189
|
+
.trim();
|
|
190
|
+
const spacer = allAttrs ? ' ' : '';
|
|
191
|
+
const openingStart = match.index;
|
|
192
|
+
const openingEnd = match.index + match[0].length;
|
|
193
|
+
let count = 1;
|
|
194
|
+
let pos = openingEnd;
|
|
195
|
+
while (pos < content.length && count > 0) {
|
|
196
|
+
if (content.startsWith(`<${tagName}`, pos)) {
|
|
197
|
+
count++;
|
|
198
|
+
}
|
|
199
|
+
else if (content.startsWith(`</${tagName}>`, pos)) {
|
|
200
|
+
count--;
|
|
201
|
+
if (count === 0) {
|
|
202
|
+
const closingStart = pos;
|
|
203
|
+
const closingEnd = pos + `</${tagName}>`.length;
|
|
204
|
+
const innerContent = content.slice(openingEnd, closingStart);
|
|
205
|
+
const replacement = `<et-menu${spacer}${allAttrs}>${innerContent}</et-menu>`;
|
|
206
|
+
replacements.push({ start: openingStart, end: closingEnd, replacement });
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
pos++;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Replace in reverse order to maintain positions
|
|
214
|
+
replacements.sort((a, b) => b.start - a.start);
|
|
215
|
+
for (const rep of replacements) {
|
|
216
|
+
content = content.slice(0, rep.start) + rep.replacement + content.slice(rep.end);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// 3. Replace cdkMenuItem directive with etMenuItem
|
|
220
|
+
if (content.includes('cdkMenuItem')) {
|
|
221
|
+
content = content.replace(/\bcdkMenuItem\b/g, 'etMenuItem');
|
|
222
|
+
}
|
|
223
|
+
// 4. Replace cdkMenuItemCheckbox with et-menu-checkbox-item
|
|
224
|
+
if (content.includes('cdkMenuItemCheckbox')) {
|
|
225
|
+
const checkboxRegex = /<(\w+)([^>]*?)\bcdkMenuItemCheckbox\b([^>]*)>([\s\S]*?)<\/\1>/g;
|
|
226
|
+
content = content.replace(checkboxRegex, (match, tagName, beforeAttrs, afterAttrs, inner) => {
|
|
227
|
+
const allAttrs = (beforeAttrs + afterAttrs)
|
|
228
|
+
.replace(/\s+cdkMenuItemCheckbox\s+/, ' ')
|
|
229
|
+
.replace(/\bcdkMenuItemCheckbox\b/, '')
|
|
230
|
+
.trim();
|
|
231
|
+
const spacer = allAttrs ? ' ' : '';
|
|
232
|
+
return `<et-menu-checkbox-item${spacer}${allAttrs}>${inner}</et-menu-checkbox-item>`;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
// 5. Replace cdkMenuItemRadio with et-menu-radio-item and add warning
|
|
236
|
+
if (content.includes('cdkMenuItemRadio')) {
|
|
237
|
+
const radioRegex = /<(\w+)([^>]*?)\bcdkMenuItemRadio\b([^>]*)>([\s\S]*?)<\/\1>/g;
|
|
238
|
+
content = content.replace(radioRegex, (match, tagName, beforeAttrs, afterAttrs, inner) => {
|
|
239
|
+
const allAttrs = (beforeAttrs + afterAttrs)
|
|
240
|
+
.replace(/\s+cdkMenuItemRadio\s+/, ' ')
|
|
241
|
+
.replace(/\bcdkMenuItemRadio\b/, '')
|
|
242
|
+
.trim();
|
|
243
|
+
const spacer = allAttrs ? ' ' : '';
|
|
244
|
+
fileWarnings.push('Manual migration required: Wrap et-menu-radio-item elements in <div etMenuRadioGroup> and add appropriate value attributes.');
|
|
245
|
+
return `<et-menu-radio-item${spacer}${allAttrs} value="TODO">${inner}</et-menu-radio-item>`;
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
if (content !== originalContent) {
|
|
249
|
+
tree.write(filePath, content);
|
|
250
|
+
}
|
|
251
|
+
if (fileWarnings.length > 0) {
|
|
252
|
+
if (!warnings.has(filePath)) {
|
|
253
|
+
warnings.set(filePath, []);
|
|
254
|
+
}
|
|
255
|
+
warnings.get(filePath).push(...fileWarnings);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=cdk-menu.js.map
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { logger, readJson, visitNotIgnoredFiles, writeJson } from '@nx/devkit';
|
|
2
|
+
// Theming-related exports that moved from @ethlete/theming and @ethlete/cdk to @ethlete/core
|
|
3
|
+
const THEMING_EXPORTS = new Set([
|
|
4
|
+
'ProvideThemeDirective',
|
|
5
|
+
'THEME_PROVIDER',
|
|
6
|
+
'Theme',
|
|
7
|
+
'ThemeSwatch',
|
|
8
|
+
'OnThemeColorMap',
|
|
9
|
+
'ThemeColorMap',
|
|
10
|
+
'ThemeColor',
|
|
11
|
+
'ThemeHSLColor',
|
|
12
|
+
'ThemeRGBColor',
|
|
13
|
+
'provideThemes',
|
|
14
|
+
'provideColorThemes',
|
|
15
|
+
]);
|
|
16
|
+
export default async function migrateColorThemes(tree) {
|
|
17
|
+
console.log('\nš Migrating provideThemes to provideColorThemes and imports to @ethlete/core');
|
|
18
|
+
let filesModified = 0;
|
|
19
|
+
let functionReplacements = 0;
|
|
20
|
+
let importsMoved = 0;
|
|
21
|
+
let importsMerged = 0;
|
|
22
|
+
let packagesRemoved = 0;
|
|
23
|
+
// Migrate TypeScript files
|
|
24
|
+
visitNotIgnoredFiles(tree, '', (filePath) => {
|
|
25
|
+
if (!filePath.endsWith('.ts')) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const content = tree.read(filePath, 'utf-8');
|
|
29
|
+
if (!content) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Check if file contains provideThemes or imports from @ethlete/theming or @ethlete/cdk
|
|
33
|
+
if (!content.includes('provideThemes') &&
|
|
34
|
+
!content.includes('@ethlete/theming') &&
|
|
35
|
+
!content.includes('@ethlete/cdk')) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
let modified = content;
|
|
39
|
+
let fileChanges = 0;
|
|
40
|
+
// Replace provideThemes with provideColorThemes
|
|
41
|
+
const beforeProvideThemesCount = (modified.match(/\bprovideThemes\b/g) || []).length;
|
|
42
|
+
modified = modified.replace(/\bprovideThemes\b/g, 'provideColorThemes');
|
|
43
|
+
fileChanges += beforeProvideThemesCount;
|
|
44
|
+
functionReplacements += beforeProvideThemesCount;
|
|
45
|
+
// Handle import merging from @ethlete/theming and @ethlete/cdk to @ethlete/core
|
|
46
|
+
const themingImportPattern = /import\s*{([^}]*)}\s*from\s*['"]@ethlete\/theming['"]/g;
|
|
47
|
+
const cdkImportPattern = /import\s*{([^}]*)}\s*from\s*['"]@ethlete\/cdk['"]/g;
|
|
48
|
+
const coreImportPattern = /import\s*{([^}]*)}\s*from\s*['"]@ethlete\/core['"]/;
|
|
49
|
+
const themingImportsToMove = [];
|
|
50
|
+
const cdkThemingImportsToMove = [];
|
|
51
|
+
const cdkNonThemingImports = [];
|
|
52
|
+
let themingMatch;
|
|
53
|
+
let cdkMatch;
|
|
54
|
+
// Collect all imports from @ethlete/theming (all should be moved)
|
|
55
|
+
while ((themingMatch = themingImportPattern.exec(modified)) !== null) {
|
|
56
|
+
const imports = themingMatch[1]
|
|
57
|
+
.split(',')
|
|
58
|
+
.map((imp) => imp.trim())
|
|
59
|
+
.filter((imp) => imp.length > 0);
|
|
60
|
+
// Keep the full import string including 'type' keyword and aliases
|
|
61
|
+
themingImportsToMove.push(...imports);
|
|
62
|
+
}
|
|
63
|
+
// Collect imports from @ethlete/cdk and separate theming vs non-theming
|
|
64
|
+
while ((cdkMatch = cdkImportPattern.exec(modified)) !== null) {
|
|
65
|
+
const imports = cdkMatch[1]
|
|
66
|
+
.split(',')
|
|
67
|
+
.map((imp) => imp.trim())
|
|
68
|
+
.filter((imp) => imp.length > 0);
|
|
69
|
+
imports.forEach((imp) => {
|
|
70
|
+
// Extract the actual import name (without 'type' keyword and alias)
|
|
71
|
+
// Handle patterns like: "type Theme as EthleteTheme", "Theme", "type Theme"
|
|
72
|
+
const typeKeywordMatch = imp.match(/^type\s+/);
|
|
73
|
+
const hasTypeKeyword = !!typeKeywordMatch;
|
|
74
|
+
const withoutType = imp.replace(/^type\s+/, '').trim();
|
|
75
|
+
const importName = withoutType.split(/\s+as\s+/)[0]?.trim() || withoutType;
|
|
76
|
+
if (THEMING_EXPORTS.has(importName)) {
|
|
77
|
+
cdkThemingImportsToMove.push(imp); // Keep the original import with type keyword and alias
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
cdkNonThemingImports.push(imp); // Keep the original import with type keyword and alias
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const allThemingImports = [...themingImportsToMove, ...cdkThemingImportsToMove];
|
|
85
|
+
if (allThemingImports.length > 0) {
|
|
86
|
+
// Check if there's already a @ethlete/core import
|
|
87
|
+
const existingCoreImport = modified.match(coreImportPattern);
|
|
88
|
+
if (existingCoreImport) {
|
|
89
|
+
// Merge theming imports with existing @ethlete/core import
|
|
90
|
+
const existingImports = existingCoreImport[1]
|
|
91
|
+
.split(',')
|
|
92
|
+
.map((imp) => imp.trim())
|
|
93
|
+
.filter((imp) => imp.length > 0);
|
|
94
|
+
const allImports = [...existingImports, ...allThemingImports];
|
|
95
|
+
// Remove duplicates while preserving order and type keywords
|
|
96
|
+
// We need to compare the actual import names (without type and aliases) for deduplication
|
|
97
|
+
const seen = new Set();
|
|
98
|
+
const uniqueImports = allImports.filter((imp) => {
|
|
99
|
+
const withoutType = imp.replace(/^type\s+/, '').trim();
|
|
100
|
+
const importName = withoutType.split(/\s+as\s+/)[0]?.trim() || withoutType;
|
|
101
|
+
if (seen.has(importName)) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
seen.add(importName);
|
|
105
|
+
return true;
|
|
106
|
+
});
|
|
107
|
+
// Replace the existing @ethlete/core import with merged imports
|
|
108
|
+
modified = modified.replace(coreImportPattern, `import { ${uniqueImports.join(', ')} } from '@ethlete/core'`);
|
|
109
|
+
importsMerged++;
|
|
110
|
+
fileChanges++;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// No existing @ethlete/core import, create a new one
|
|
114
|
+
const uniqueImports = Array.from(new Set(allThemingImports));
|
|
115
|
+
// Find the position of the first theming import to replace
|
|
116
|
+
const firstThemingImport = modified.match(/import\s*{[^}]*}\s*from\s*['"]@ethlete\/theming['"];?\s*\n?/);
|
|
117
|
+
if (firstThemingImport) {
|
|
118
|
+
// Replace first theming import with core import
|
|
119
|
+
modified = modified.replace(/import\s*{[^}]*}\s*from\s*['"]@ethlete\/theming['"];?\s*\n?/, `import { ${uniqueImports.join(', ')} } from '@ethlete/core';\n`);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// Add new @ethlete/core import at the beginning (after first import)
|
|
123
|
+
const firstImportMatch = modified.match(/import\s+[^;]+;/);
|
|
124
|
+
if (firstImportMatch) {
|
|
125
|
+
const insertPosition = firstImportMatch.index + firstImportMatch[0].length;
|
|
126
|
+
modified =
|
|
127
|
+
modified.slice(0, insertPosition) +
|
|
128
|
+
`\nimport { ${uniqueImports.join(', ')} } from '@ethlete/core';` +
|
|
129
|
+
modified.slice(insertPosition);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
importsMoved++;
|
|
133
|
+
fileChanges++;
|
|
134
|
+
}
|
|
135
|
+
// Remove all @ethlete/theming imports
|
|
136
|
+
modified = modified.replace(/import\s*{[^}]*}\s*from\s*['"]@ethlete\/theming['"];?\s*\n?/g, '');
|
|
137
|
+
// Handle @ethlete/cdk imports - only process if we found theming imports to move
|
|
138
|
+
if (cdkThemingImportsToMove.length > 0) {
|
|
139
|
+
// Remove all existing @ethlete/cdk imports first
|
|
140
|
+
modified = modified.replace(/import\s*{[^}]*}\s*from\s*['"]@ethlete\/cdk['"];?\s*\n?/g, '');
|
|
141
|
+
if (cdkNonThemingImports.length > 0) {
|
|
142
|
+
// Add back only the non-theming imports after the @ethlete/core import
|
|
143
|
+
const cdkImportStatement = `import { ${cdkNonThemingImports.join(', ')} } from '@ethlete/cdk';\n`;
|
|
144
|
+
const coreImportMatch = modified.match(/import\s*{[^}]*}\s*from\s*['"]@ethlete\/core['"];?\s*\n?/);
|
|
145
|
+
if (coreImportMatch) {
|
|
146
|
+
const insertPosition = coreImportMatch.index + coreImportMatch[0].length;
|
|
147
|
+
modified = modified.slice(0, insertPosition) + cdkImportStatement + modified.slice(insertPosition);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
// If no core import exists, add after the first import
|
|
151
|
+
const firstImportMatch = modified.match(/import\s+[^;]+;/);
|
|
152
|
+
if (firstImportMatch) {
|
|
153
|
+
const insertPosition = firstImportMatch.index + firstImportMatch[0].length;
|
|
154
|
+
modified = modified.slice(0, insertPosition) + '\n' + cdkImportStatement + modified.slice(insertPosition);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Clean up any resulting double newlines
|
|
159
|
+
modified = modified.replace(/\n\n\n+/g, '\n\n');
|
|
160
|
+
fileChanges++;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (modified !== content) {
|
|
164
|
+
tree.write(filePath, modified);
|
|
165
|
+
filesModified++;
|
|
166
|
+
console.log(` ā ${filePath} (${fileChanges} change${fileChanges !== 1 ? 's' : ''})`);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
// Remove @ethlete/theming from package.json files (but keep @ethlete/cdk)
|
|
170
|
+
const packageJsonFiles = ['package.json'];
|
|
171
|
+
// Also check for package.json in apps and libs
|
|
172
|
+
visitNotIgnoredFiles(tree, '', (filePath) => {
|
|
173
|
+
if (filePath.endsWith('package.json') && !packageJsonFiles.includes(filePath)) {
|
|
174
|
+
packageJsonFiles.push(filePath);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
for (const packageJsonPath of packageJsonFiles) {
|
|
178
|
+
if (!tree.exists(packageJsonPath)) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
const packageJson = readJson(tree, packageJsonPath);
|
|
183
|
+
let modified = false;
|
|
184
|
+
// Remove @ethlete/theming from dependencies
|
|
185
|
+
if (packageJson.dependencies && packageJson.dependencies['@ethlete/theming']) {
|
|
186
|
+
delete packageJson.dependencies['@ethlete/theming'];
|
|
187
|
+
modified = true;
|
|
188
|
+
console.log(` ā Removed @ethlete/theming from ${packageJsonPath} dependencies`);
|
|
189
|
+
}
|
|
190
|
+
// Remove @ethlete/theming from devDependencies
|
|
191
|
+
if (packageJson.devDependencies && packageJson.devDependencies['@ethlete/theming']) {
|
|
192
|
+
delete packageJson.devDependencies['@ethlete/theming'];
|
|
193
|
+
modified = true;
|
|
194
|
+
console.log(` ā Removed @ethlete/theming from ${packageJsonPath} devDependencies`);
|
|
195
|
+
}
|
|
196
|
+
// Remove @ethlete/theming from peerDependencies
|
|
197
|
+
if (packageJson.peerDependencies && packageJson.peerDependencies['@ethlete/theming']) {
|
|
198
|
+
delete packageJson.peerDependencies['@ethlete/theming'];
|
|
199
|
+
modified = true;
|
|
200
|
+
console.log(` ā Removed @ethlete/theming from ${packageJsonPath} peerDependencies`);
|
|
201
|
+
}
|
|
202
|
+
if (modified) {
|
|
203
|
+
writeJson(tree, packageJsonPath, packageJson);
|
|
204
|
+
packagesRemoved++;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
logger.warn(`Failed to process ${packageJsonPath}: ${error}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
console.log(`\nā
Color themes migration complete:`);
|
|
212
|
+
console.log(` š Files modified: ${filesModified}`);
|
|
213
|
+
console.log(` š Function replacements: ${functionReplacements}`);
|
|
214
|
+
console.log(` š¦ Imports moved: ${importsMoved}`);
|
|
215
|
+
console.log(` š Imports merged: ${importsMerged}`);
|
|
216
|
+
console.log(` šļø Package.json files updated: ${packagesRemoved}`);
|
|
217
|
+
if (packagesRemoved > 0) {
|
|
218
|
+
console.log(`\nā ļø Don't forget to run 'yarn install' to update your lock file!`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=color-themes.js.map
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { visitNotIgnoredFiles } from '@nx/devkit';
|
|
2
|
+
import * as ts from 'typescript';
|
|
3
|
+
export function migrateCombobox(tree) {
|
|
4
|
+
console.log('\nš Migrating <et-combobox> components...');
|
|
5
|
+
migrateInputs(tree);
|
|
6
|
+
migrateProvideComboboxConfig(tree);
|
|
7
|
+
}
|
|
8
|
+
function migrateInputs(tree) {
|
|
9
|
+
let filesModified = 0;
|
|
10
|
+
let propertiesRenamed = 0;
|
|
11
|
+
visitNotIgnoredFiles(tree, '', (filePath) => {
|
|
12
|
+
if (!filePath.endsWith('.html') && !filePath.endsWith('.component.ts')) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const content = tree.read(filePath, 'utf-8');
|
|
16
|
+
if (!content)
|
|
17
|
+
return;
|
|
18
|
+
const newContent = migrateEmptyTextProperty(content, filePath);
|
|
19
|
+
if (newContent !== content) {
|
|
20
|
+
tree.write(filePath, newContent);
|
|
21
|
+
filesModified++;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
function migrateEmptyTextProperty(content, filePath) {
|
|
25
|
+
let result = content;
|
|
26
|
+
let filePropertiesRenamed = 0;
|
|
27
|
+
// HTML template files
|
|
28
|
+
if (filePath.endsWith('.html')) {
|
|
29
|
+
// Find all <et-combobox> tags and only replace emptyText within them
|
|
30
|
+
const comboboxRegex = /<et-combobox\s[^>]*>/g;
|
|
31
|
+
result = content.replace(comboboxRegex, (match) => {
|
|
32
|
+
let modified = match;
|
|
33
|
+
let localRenames = 0;
|
|
34
|
+
// Replace [emptyText] with [bodyEmptyText]
|
|
35
|
+
const propertyBindingCount = (modified.match(/\[emptyText\]/g) || []).length;
|
|
36
|
+
modified = modified.replace(/\[emptyText\]/g, '[bodyEmptyText]');
|
|
37
|
+
localRenames += propertyBindingCount;
|
|
38
|
+
// Replace emptyText= with bodyEmptyText= (attribute binding)
|
|
39
|
+
const attributeCount = (modified.match(/(\s)emptyText=/g) || []).length;
|
|
40
|
+
modified = modified.replace(/(\s)emptyText=/g, '$1bodyEmptyText=');
|
|
41
|
+
localRenames += attributeCount;
|
|
42
|
+
if (localRenames > 0) {
|
|
43
|
+
filePropertiesRenamed += localRenames;
|
|
44
|
+
}
|
|
45
|
+
return modified;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// TypeScript component files (inline templates)
|
|
49
|
+
if (filePath.endsWith('.component.ts')) {
|
|
50
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
51
|
+
const replacements = [];
|
|
52
|
+
function visit(node) {
|
|
53
|
+
// Handle template strings in @Component decorator
|
|
54
|
+
if (ts.isPropertyAssignment(node)) {
|
|
55
|
+
if (ts.isIdentifier(node.name) &&
|
|
56
|
+
node.name.text === 'template' &&
|
|
57
|
+
(ts.isStringLiteral(node.initializer) || ts.isNoSubstitutionTemplateLiteral(node.initializer))) {
|
|
58
|
+
const templateContent = node.initializer.getText(sourceFile);
|
|
59
|
+
const templateText = templateContent.slice(1, -1); // Remove quotes
|
|
60
|
+
// Only process if template contains et-combobox
|
|
61
|
+
if (templateText.includes('<et-combobox') && templateText.includes('emptyText')) {
|
|
62
|
+
let newTemplate = templateText;
|
|
63
|
+
let localRenames = 0;
|
|
64
|
+
// Find all <et-combobox> tags and only replace emptyText within them
|
|
65
|
+
const comboboxRegex = /<et-combobox\s[^>]*>/g;
|
|
66
|
+
newTemplate = newTemplate.replace(comboboxRegex, (match) => {
|
|
67
|
+
let modified = match;
|
|
68
|
+
// Replace [emptyText] with [bodyEmptyText]
|
|
69
|
+
const propertyBindingCount = (modified.match(/\[emptyText\]/g) || []).length;
|
|
70
|
+
modified = modified.replace(/\[emptyText\]/g, '[bodyEmptyText]');
|
|
71
|
+
localRenames += propertyBindingCount;
|
|
72
|
+
// Replace emptyText= with bodyEmptyText=
|
|
73
|
+
const attributeCount = (modified.match(/(\s)emptyText=/g) || []).length;
|
|
74
|
+
modified = modified.replace(/(\s)emptyText=/g, '$1bodyEmptyText=');
|
|
75
|
+
localRenames += attributeCount;
|
|
76
|
+
return modified;
|
|
77
|
+
});
|
|
78
|
+
if (localRenames > 0) {
|
|
79
|
+
const quote = templateContent[0];
|
|
80
|
+
replacements.push({
|
|
81
|
+
start: node.initializer.getStart(sourceFile),
|
|
82
|
+
end: node.initializer.getEnd(),
|
|
83
|
+
replacement: `${quote}${newTemplate}${quote}`,
|
|
84
|
+
});
|
|
85
|
+
filePropertiesRenamed += localRenames;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
ts.forEachChild(node, visit);
|
|
91
|
+
}
|
|
92
|
+
visit(sourceFile);
|
|
93
|
+
// Apply replacements in reverse order
|
|
94
|
+
if (replacements.length > 0) {
|
|
95
|
+
replacements.sort((a, b) => b.start - a.start);
|
|
96
|
+
for (const { start, end, replacement } of replacements) {
|
|
97
|
+
result = result.slice(0, start) + replacement + result.slice(end);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (filePropertiesRenamed > 0) {
|
|
102
|
+
console.log(` ā ${filePath}: renamed ${filePropertiesRenamed} property(ies)`);
|
|
103
|
+
propertiesRenamed += filePropertiesRenamed;
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
if (filesModified > 0) {
|
|
108
|
+
console.log(`\nā
Migrated ${filesModified} file(s), renamed ${propertiesRenamed} emptyText property(ies) to bodyEmptyText`);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
console.log('\nā
No <et-combobox> components found that need migration');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function migrateProvideComboboxConfig(tree) {
|
|
115
|
+
let filesModified = 0;
|
|
116
|
+
let configCallsUpdated = 0;
|
|
117
|
+
visitNotIgnoredFiles(tree, '', (filePath) => {
|
|
118
|
+
if (!filePath.endsWith('.ts')) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const content = tree.read(filePath, 'utf-8');
|
|
122
|
+
if (!content)
|
|
123
|
+
return;
|
|
124
|
+
const result = migrateConfigCalls(content, filePath);
|
|
125
|
+
if (result.content !== content) {
|
|
126
|
+
tree.write(filePath, result.content);
|
|
127
|
+
filesModified++;
|
|
128
|
+
configCallsUpdated += result.updatedCount;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
function migrateConfigCalls(content, filePath) {
|
|
132
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
133
|
+
const replacements = [];
|
|
134
|
+
let configsUpdated = 0;
|
|
135
|
+
function visit(node) {
|
|
136
|
+
// Find provideComboboxConfig() calls
|
|
137
|
+
if (ts.isCallExpression(node)) {
|
|
138
|
+
if (ts.isIdentifier(node.expression) &&
|
|
139
|
+
node.expression.text === 'provideComboboxConfig' &&
|
|
140
|
+
node.arguments.length > 0) {
|
|
141
|
+
const configArg = node.arguments[0];
|
|
142
|
+
// Check if the argument is an object literal
|
|
143
|
+
if (ts.isObjectLiteralExpression(configArg)) {
|
|
144
|
+
const emptyTextProp = configArg.properties.find((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === 'emptyText');
|
|
145
|
+
if (emptyTextProp) {
|
|
146
|
+
// Check if bodyEmptyText already exists
|
|
147
|
+
const bodyEmptyTextProp = configArg.properties.find((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === 'bodyEmptyText');
|
|
148
|
+
if (!bodyEmptyTextProp) {
|
|
149
|
+
// Rename emptyText to bodyEmptyText
|
|
150
|
+
const propStart = emptyTextProp.name.getStart(sourceFile);
|
|
151
|
+
const propEnd = emptyTextProp.name.getEnd();
|
|
152
|
+
replacements.push({
|
|
153
|
+
start: propStart,
|
|
154
|
+
end: propEnd,
|
|
155
|
+
replacement: 'bodyEmptyText',
|
|
156
|
+
});
|
|
157
|
+
configsUpdated++;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
ts.forEachChild(node, visit);
|
|
164
|
+
}
|
|
165
|
+
visit(sourceFile);
|
|
166
|
+
let result = content;
|
|
167
|
+
// Apply replacements in reverse order
|
|
168
|
+
if (replacements.length > 0) {
|
|
169
|
+
replacements.sort((a, b) => b.start - a.start);
|
|
170
|
+
for (const { start, end, replacement } of replacements) {
|
|
171
|
+
result = result.slice(0, start) + replacement + result.slice(end);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (configsUpdated > 0) {
|
|
175
|
+
console.log(` ā ${filePath}: updated ${configsUpdated} provideComboboxConfig call(s)`);
|
|
176
|
+
}
|
|
177
|
+
return { content: result, updatedCount: configsUpdated };
|
|
178
|
+
}
|
|
179
|
+
if (filesModified > 0) {
|
|
180
|
+
console.log(`\nā
Migrated ${filesModified} file(s), updated ${configCallsUpdated} config call(s)`);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
console.log('\nā
No provideComboboxConfig calls found that need migration');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=combobox.js.map
|