@ethlete/core 5.0.0-next.6 ā 5.0.0-next.60
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 +686 -0
- package/README.md +28 -4
- package/fesm2022/ethlete-core.mjs +8020 -3527
- package/fesm2022/ethlete-core.mjs.map +1 -1
- package/generators/devtools-about/generator.js +122 -0
- package/generators/devtools-about/schema.json +25 -0
- package/generators/generators.json +26 -5
- package/generators/migrate-provider-shape/migration-scope.js +35 -0
- package/generators/migrate-provider-shape/migration.js +50 -0
- package/generators/migrate-provider-shape/provider-shape.js +284 -0
- package/generators/migrate-provider-shape/schema.json +26 -0
- package/generators/migrate-surface-interaction-swatch/interaction-swatch.js +63 -0
- package/generators/migrate-surface-interaction-swatch/migration.js +36 -0
- package/generators/migrate-surface-interaction-swatch/schema.json +26 -0
- package/generators/migrate-to-v5/color-naming.js +170 -0
- package/generators/migrate-to-v5/create-provider.js +14 -14
- package/generators/migrate-to-v5/migration.js +11 -1
- package/generators/migrate-to-v5/removed-exports.js +93 -0
- package/generators/migrate-to-v5/router-state-service.js +33 -8
- package/generators/migrate-to-v5/schema.json +10 -0
- package/generators/migrate-to-v5/viewport-service.js +41 -60
- package/generators/tailwind-4-color-theme/generator.js +608 -0
- package/generators/tailwind-4-color-theme/schema.json +44 -0
- package/generators/tailwind-4-surface-theme/generator.js +599 -0
- package/generators/tailwind-4-surface-theme/schema.json +48 -0
- package/migrations/seo-directive-removed.md +80 -0
- package/migrations/surface-theme-regenerate.md +20 -0
- package/migrations.json +43 -0
- package/package.json +25 -14
- package/types/ethlete-core.d.ts +3329 -1774
- package/generators/tailwind-4-theme/generator.js +0 -490
- package/generators/tailwind-4-theme/schema.json +0 -32
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { formatFiles } from '@nx/devkit';
|
|
2
|
+
import { createMigrationScope } from '../migrate-provider-shape/migration-scope.js';
|
|
3
|
+
import { migrateInteractionSwatchInFile } from './interaction-swatch.js';
|
|
4
|
+
export default async function migrateSurfaceInteractionSwatch(tree, schema) {
|
|
5
|
+
console.log('\nš Migrating surface interactionColor to the swatch shape...');
|
|
6
|
+
const scope = createMigrationScope(tree, schema);
|
|
7
|
+
console.log(` Scope: ${scope.describe()}`);
|
|
8
|
+
let filesChanged = 0;
|
|
9
|
+
let unresolvedCount = 0;
|
|
10
|
+
scope.visit(tree, (filePath) => {
|
|
11
|
+
if (!filePath.endsWith('.ts') || filePath.endsWith('.d.ts'))
|
|
12
|
+
return;
|
|
13
|
+
const before = tree.read(filePath, 'utf-8');
|
|
14
|
+
if (!before)
|
|
15
|
+
return;
|
|
16
|
+
const result = migrateInteractionSwatchInFile(filePath, before);
|
|
17
|
+
for (const expression of result.unresolved ?? []) {
|
|
18
|
+
unresolvedCount++;
|
|
19
|
+
console.warn(` ā ${filePath}: could not resolve interactionColor value ${expression}`);
|
|
20
|
+
}
|
|
21
|
+
if (!result.changed)
|
|
22
|
+
return;
|
|
23
|
+
tree.write(filePath, result.content);
|
|
24
|
+
filesChanged++;
|
|
25
|
+
console.log(` ā ${filePath}`);
|
|
26
|
+
});
|
|
27
|
+
console.log(filesChanged === 0
|
|
28
|
+
? ' Nothing to migrate.\n'
|
|
29
|
+
: `\nā
Migrated ${filesChanged} file(s). Regenerate your surface CSS with \`nx g @ethlete/core:tailwind-4-surface-theme\`.\n`);
|
|
30
|
+
if (unresolvedCount > 0) {
|
|
31
|
+
console.warn(` Review ${unresolvedCount} unresolved interactionColor value(s) manually.\n`);
|
|
32
|
+
}
|
|
33
|
+
if (!schema.skipFormat) {
|
|
34
|
+
await formatFiles(tree);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"$id": "migrate-surface-interaction-swatch",
|
|
4
|
+
"title": "Migrate surface interactionColor to the swatch shape",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"projects": {
|
|
8
|
+
"type": "array",
|
|
9
|
+
"items": { "type": "string" },
|
|
10
|
+
"description": "Nx projects to migrate. Defaults to every project in the workspace.",
|
|
11
|
+
"alias": "project"
|
|
12
|
+
},
|
|
13
|
+
"include": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"items": { "type": "string" },
|
|
16
|
+
"description": "Path prefixes to migrate, relative to the workspace root (e.g. 'apps/web'). Unioned with `projects`. Defaults to the whole workspace.",
|
|
17
|
+
"alias": "includePath"
|
|
18
|
+
},
|
|
19
|
+
"skipFormat": {
|
|
20
|
+
"type": "boolean",
|
|
21
|
+
"description": "Skip formatting files after migration",
|
|
22
|
+
"default": false
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"required": []
|
|
26
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { logger } from '@nx/devkit';
|
|
2
|
+
/**
|
|
3
|
+
* Symbol renames in TypeScript files (imports + usage).
|
|
4
|
+
*/
|
|
5
|
+
const TS_SYMBOL_RENAMES = {
|
|
6
|
+
ProvideThemeDirective: 'ProvideColorDirective',
|
|
7
|
+
THEME_PROVIDER: 'COLOR_PROVIDER',
|
|
8
|
+
ColorThemedDirective: 'ColorInteractiveDirective',
|
|
9
|
+
ColorThemedStylesComponent: 'ColorInteractiveStylesComponent',
|
|
10
|
+
SurfaceThemedDirective: 'SurfaceInteractiveDirective',
|
|
11
|
+
SurfaceThemedStylesComponent: 'SurfaceInteractiveStylesComponent',
|
|
12
|
+
injectThemesPrefix: 'injectColorThemesPrefix',
|
|
13
|
+
createCssThemeName: 'createCssColorThemeName',
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* String replacements applied to HTML templates and inline templates.
|
|
17
|
+
*/
|
|
18
|
+
const TEMPLATE_REPLACEMENTS = [
|
|
19
|
+
['[etProvideTheme]', '[etProvideColor]'],
|
|
20
|
+
['etProvideTheme', 'etProvideColor'],
|
|
21
|
+
['[etColorThemed]', '[etColorInteractive]'],
|
|
22
|
+
['etColorThemed', 'etColorInteractive'],
|
|
23
|
+
['[etSurfaceThemed]', '[etSurfaceInteractive]'],
|
|
24
|
+
['etSurfaceThemed', 'etSurfaceInteractive'],
|
|
25
|
+
['et-theme-alt--', 'et-color-alt--'],
|
|
26
|
+
['et-theme--', 'et-color--'],
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* CSS class name replacements.
|
|
30
|
+
*/
|
|
31
|
+
const CSS_REPLACEMENTS = [
|
|
32
|
+
['.et-color-themed', '.et-color-interactive'],
|
|
33
|
+
['.et-surface-themed', '.et-surface-interactive'],
|
|
34
|
+
['.et-theme--', '.et-color--'],
|
|
35
|
+
['.et-theme-alt--', '.et-color-alt--'],
|
|
36
|
+
];
|
|
37
|
+
/**
|
|
38
|
+
* Host directive input alias replacements in TypeScript files.
|
|
39
|
+
*/
|
|
40
|
+
const HOST_DIRECTIVE_REPLACEMENTS = [
|
|
41
|
+
["'etProvideTheme:theme'", "'etProvideColor:color'"],
|
|
42
|
+
["'etProvideTheme:color'", "'etProvideColor:color'"],
|
|
43
|
+
['"etProvideTheme:theme"', '"etProvideColor:color"'],
|
|
44
|
+
['"etProvideTheme"', '"etProvideColor"'],
|
|
45
|
+
["'etProvideTheme: theme'", "'etProvideColor: color'"],
|
|
46
|
+
['"etProvideTheme: theme"', '"etProvideColor: color"'],
|
|
47
|
+
];
|
|
48
|
+
export default async function migrateColorNaming(tree) {
|
|
49
|
+
logger.log('\nš Migrating theme ā color naming...\n');
|
|
50
|
+
const tsFiles = [];
|
|
51
|
+
const htmlFiles = [];
|
|
52
|
+
const cssFiles = [];
|
|
53
|
+
findFiles('.', tree, tsFiles, htmlFiles, cssFiles);
|
|
54
|
+
let filesModified = 0;
|
|
55
|
+
for (const filePath of tsFiles) {
|
|
56
|
+
const wasModified = migrateTypeScriptFile(tree, filePath);
|
|
57
|
+
if (wasModified) {
|
|
58
|
+
filesModified++;
|
|
59
|
+
logger.log(` ā ${filePath}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
for (const filePath of htmlFiles) {
|
|
63
|
+
const wasModified = migrateTemplateFile(tree, filePath);
|
|
64
|
+
if (wasModified) {
|
|
65
|
+
filesModified++;
|
|
66
|
+
logger.log(` ā ${filePath}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
for (const filePath of cssFiles) {
|
|
70
|
+
const wasModified = migrateCssFile(tree, filePath);
|
|
71
|
+
if (wasModified) {
|
|
72
|
+
filesModified++;
|
|
73
|
+
logger.log(` ā ${filePath}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (filesModified > 0) {
|
|
77
|
+
logger.log(`\nā
Successfully migrated color naming in ${filesModified} file(s)\n`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
logger.log('\nā¹ļø No files needed migration\n');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function findFiles(dir, tree, tsFiles, htmlFiles, cssFiles) {
|
|
84
|
+
const children = tree.children(dir);
|
|
85
|
+
for (const child of children) {
|
|
86
|
+
const path = dir === '.' ? child : `${dir}/${child}`;
|
|
87
|
+
if (tree.isFile(path)) {
|
|
88
|
+
if (path.includes('node_modules'))
|
|
89
|
+
continue;
|
|
90
|
+
if (path.endsWith('.ts')) {
|
|
91
|
+
tsFiles.push(path);
|
|
92
|
+
}
|
|
93
|
+
else if (path.endsWith('.html')) {
|
|
94
|
+
htmlFiles.push(path);
|
|
95
|
+
}
|
|
96
|
+
else if (path.endsWith('.css') || path.endsWith('.scss')) {
|
|
97
|
+
cssFiles.push(path);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
if (child === 'node_modules')
|
|
102
|
+
continue;
|
|
103
|
+
findFiles(path, tree, tsFiles, htmlFiles, cssFiles);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function migrateTypeScriptFile(tree, filePath) {
|
|
108
|
+
let content = tree.read(filePath, 'utf-8');
|
|
109
|
+
if (!content)
|
|
110
|
+
return false;
|
|
111
|
+
const original = content;
|
|
112
|
+
if (!/from\s+['"]@ethlete\/(?:core|cdk)['"]/.test(content))
|
|
113
|
+
return false;
|
|
114
|
+
// Rename symbols
|
|
115
|
+
for (const [oldName, newName] of Object.entries(TS_SYMBOL_RENAMES)) {
|
|
116
|
+
content = replaceWholeWord(content, oldName, newName);
|
|
117
|
+
}
|
|
118
|
+
// Rename host directive input aliases
|
|
119
|
+
for (const [oldStr, newStr] of HOST_DIRECTIVE_REPLACEMENTS) {
|
|
120
|
+
content = content.replaceAll(oldStr, newStr);
|
|
121
|
+
}
|
|
122
|
+
// Handle inline templates in TypeScript files
|
|
123
|
+
for (const [oldStr, newStr] of TEMPLATE_REPLACEMENTS) {
|
|
124
|
+
content = content.replaceAll(oldStr, newStr);
|
|
125
|
+
}
|
|
126
|
+
// Handle CSS in TypeScript files
|
|
127
|
+
for (const [oldStr, newStr] of CSS_REPLACEMENTS) {
|
|
128
|
+
content = content.replaceAll(oldStr, newStr);
|
|
129
|
+
}
|
|
130
|
+
if (content !== original) {
|
|
131
|
+
tree.write(filePath, content);
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
function migrateTemplateFile(tree, filePath) {
|
|
137
|
+
let content = tree.read(filePath, 'utf-8');
|
|
138
|
+
if (!content)
|
|
139
|
+
return false;
|
|
140
|
+
const original = content;
|
|
141
|
+
for (const [oldStr, newStr] of TEMPLATE_REPLACEMENTS) {
|
|
142
|
+
content = content.replaceAll(oldStr, newStr);
|
|
143
|
+
}
|
|
144
|
+
if (content !== original) {
|
|
145
|
+
tree.write(filePath, content);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
function migrateCssFile(tree, filePath) {
|
|
151
|
+
let content = tree.read(filePath, 'utf-8');
|
|
152
|
+
if (!content)
|
|
153
|
+
return false;
|
|
154
|
+
const original = content;
|
|
155
|
+
for (const [oldStr, newStr] of CSS_REPLACEMENTS) {
|
|
156
|
+
content = content.replaceAll(oldStr, newStr);
|
|
157
|
+
}
|
|
158
|
+
if (content !== original) {
|
|
159
|
+
tree.write(filePath, content);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
function replaceWholeWord(content, oldWord, newWord) {
|
|
165
|
+
const regex = new RegExp(`\\b${escapeRegex(oldWord)}\\b`, 'g');
|
|
166
|
+
return content.replace(regex, newWord);
|
|
167
|
+
}
|
|
168
|
+
function escapeRegex(str) {
|
|
169
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
170
|
+
}
|
|
@@ -9,11 +9,13 @@ export default async function migrateCreateProvider(tree) {
|
|
|
9
9
|
for (const child of children) {
|
|
10
10
|
const path = dir === '.' ? child : `${dir}/${child}`;
|
|
11
11
|
if (tree.isFile(path)) {
|
|
12
|
-
if (path.endsWith('.ts') && !path.includes('node_modules')
|
|
12
|
+
if (path.endsWith('.ts') && !path.includes('node_modules')) {
|
|
13
13
|
tsFiles.push(path);
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
else {
|
|
17
|
+
if (child === 'node_modules')
|
|
18
|
+
continue;
|
|
17
19
|
findTsFiles(path);
|
|
18
20
|
}
|
|
19
21
|
}
|
|
@@ -40,8 +42,8 @@ function migrateCreateProviderInFile(tree, filePath) {
|
|
|
40
42
|
return false;
|
|
41
43
|
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
42
44
|
let cdkImport;
|
|
43
|
-
let coreImport;
|
|
44
45
|
let hasCreateProviderInCdk = false;
|
|
46
|
+
let createProviderImport = 'createProvider';
|
|
45
47
|
// Find relevant imports
|
|
46
48
|
for (const statement of sourceFile.statements) {
|
|
47
49
|
if (ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier)) {
|
|
@@ -49,12 +51,11 @@ function migrateCreateProviderInFile(tree, filePath) {
|
|
|
49
51
|
if (modulePath === '@ethlete/cdk') {
|
|
50
52
|
cdkImport = statement;
|
|
51
53
|
if (statement.importClause?.namedBindings && ts.isNamedImports(statement.importClause.namedBindings)) {
|
|
52
|
-
|
|
54
|
+
const specifier = statement.importClause.namedBindings.elements.find((element) => (element.propertyName?.text ?? element.name.text) === 'createProvider');
|
|
55
|
+
hasCreateProviderInCdk = !!specifier;
|
|
56
|
+
createProviderImport = specifier?.getText(sourceFile) ?? createProviderImport;
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
|
-
else if (modulePath === '@ethlete/core') {
|
|
56
|
-
coreImport = statement;
|
|
57
|
-
}
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
// If createProvider is not imported from @ethlete/cdk, nothing to do
|
|
@@ -64,7 +65,7 @@ function migrateCreateProviderInFile(tree, filePath) {
|
|
|
64
65
|
// Remove createProvider from @ethlete/cdk import
|
|
65
66
|
if (cdkImport && cdkImport.importClause?.namedBindings && ts.isNamedImports(cdkImport.importClause.namedBindings)) {
|
|
66
67
|
const namedBindings = cdkImport.importClause.namedBindings;
|
|
67
|
-
const otherImports = namedBindings.elements.filter((element) => element.name.text !== 'createProvider');
|
|
68
|
+
const otherImports = namedBindings.elements.filter((element) => (element.propertyName?.text ?? element.name.text) !== 'createProvider');
|
|
68
69
|
const importStart = cdkImport.getStart(sourceFile);
|
|
69
70
|
const importEnd = cdkImport.getEnd();
|
|
70
71
|
if (otherImports.length === 0) {
|
|
@@ -84,7 +85,7 @@ function migrateCreateProviderInFile(tree, filePath) {
|
|
|
84
85
|
}
|
|
85
86
|
else {
|
|
86
87
|
// Keep other imports
|
|
87
|
-
const newImports = otherImports.map((el) => el.
|
|
88
|
+
const newImports = otherImports.map((el) => el.getText(sourceFile)).join(', ');
|
|
88
89
|
const newImportText = `import { ${newImports} } from '@ethlete/cdk';`;
|
|
89
90
|
updatedContent = content.slice(0, importStart) + newImportText + content.slice(importEnd);
|
|
90
91
|
}
|
|
@@ -106,12 +107,12 @@ function migrateCreateProviderInFile(tree, filePath) {
|
|
|
106
107
|
ts.isNamedImports(updatedCoreImport.importClause.namedBindings)) {
|
|
107
108
|
const namedBindings = updatedCoreImport.importClause.namedBindings;
|
|
108
109
|
// Check if createProvider already exists
|
|
109
|
-
const hasCreateProvider = namedBindings.elements.some((element) => element.name.text === 'createProvider');
|
|
110
|
+
const hasCreateProvider = namedBindings.elements.some((element) => (element.propertyName?.text ?? element.name.text) === 'createProvider');
|
|
110
111
|
if (!hasCreateProvider) {
|
|
111
112
|
const importStart = updatedCoreImport.getStart(intermediateSourceFile);
|
|
112
113
|
const importEnd = updatedCoreImport.getEnd();
|
|
113
|
-
const existingImports = namedBindings.elements.map((el) => el.
|
|
114
|
-
const allImports = [...existingImports,
|
|
114
|
+
const existingImports = namedBindings.elements.map((el) => el.getText(intermediateSourceFile));
|
|
115
|
+
const allImports = [...existingImports, createProviderImport].sort();
|
|
115
116
|
const newImportText = `import { ${allImports.join(', ')} } from '@ethlete/core';`;
|
|
116
117
|
updatedContent = updatedContent.slice(0, importStart) + newImportText + updatedContent.slice(importEnd);
|
|
117
118
|
}
|
|
@@ -141,12 +142,12 @@ function migrateCreateProviderInFile(tree, filePath) {
|
|
|
141
142
|
}
|
|
142
143
|
updatedContent =
|
|
143
144
|
updatedContent.slice(0, insertPosition) +
|
|
144
|
-
|
|
145
|
+
`import { ${createProviderImport} } from '@ethlete/core';\n` +
|
|
145
146
|
updatedContent.slice(insertPosition);
|
|
146
147
|
}
|
|
147
148
|
else {
|
|
148
149
|
// No imports, add at the beginning
|
|
149
|
-
updatedContent =
|
|
150
|
+
updatedContent = `import { ${createProviderImport} } from '@ethlete/core';\n\n` + updatedContent;
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
if (updatedContent !== content) {
|
|
@@ -155,4 +156,3 @@ function migrateCreateProviderInFile(tree, filePath) {
|
|
|
155
156
|
}
|
|
156
157
|
return false;
|
|
157
158
|
}
|
|
158
|
-
//# sourceMappingURL=create-provider.js.map
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { formatFiles } from '@nx/devkit';
|
|
2
|
+
import migrateColorNaming from './color-naming.js';
|
|
2
3
|
import migrateCreateProvider from './create-provider.js';
|
|
4
|
+
import reportRemovedExports from './removed-exports.js';
|
|
3
5
|
import migrateRouterStateService from './router-state-service.js';
|
|
4
6
|
import migrateViewportService from './viewport-service.js';
|
|
5
7
|
export default async function migrate(tree, schema) {
|
|
@@ -18,6 +20,15 @@ export default async function migrate(tree, schema) {
|
|
|
18
20
|
if (shouldMigrateRouterStateService) {
|
|
19
21
|
await migrateRouterStateService(tree);
|
|
20
22
|
}
|
|
23
|
+
const shouldMigrateColorNaming = schema.migrateColorNaming !== false;
|
|
24
|
+
if (shouldMigrateColorNaming) {
|
|
25
|
+
console.log(' ⢠Migrating theme ā color naming...');
|
|
26
|
+
await migrateColorNaming(tree);
|
|
27
|
+
}
|
|
28
|
+
if (schema.reportRemovedExports !== false) {
|
|
29
|
+
console.log(' ⢠Reporting removed exports...');
|
|
30
|
+
await reportRemovedExports(tree);
|
|
31
|
+
}
|
|
21
32
|
if (!schema.skipFormat) {
|
|
22
33
|
console.log(' ⢠Formatting files...');
|
|
23
34
|
await formatFiles(tree);
|
|
@@ -25,4 +36,3 @@ export default async function migrate(tree, schema) {
|
|
|
25
36
|
console.log('\nā
Migration completed successfully!');
|
|
26
37
|
}
|
|
27
38
|
//#endregion
|
|
28
|
-
//# sourceMappingURL=migration.js.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
const REMOVED_EXPORTS = new Map([
|
|
3
|
+
...['Memo', 'MemoConfig', 'MemoResolver', 'MapLike'].map((name) => [name, 'the @Memo decorator']),
|
|
4
|
+
...[
|
|
5
|
+
'createProps',
|
|
6
|
+
'createHostProps',
|
|
7
|
+
'createPropHandlers',
|
|
8
|
+
'createSetup',
|
|
9
|
+
'createDependencyStash',
|
|
10
|
+
'createElementDictionary',
|
|
11
|
+
'bindProps',
|
|
12
|
+
'unbindProps',
|
|
13
|
+
'PropsDirective',
|
|
14
|
+
'templateComputed',
|
|
15
|
+
'ComponentType',
|
|
16
|
+
'AnyTemplateType',
|
|
17
|
+
'TemplateRefWithContext',
|
|
18
|
+
'ComponentTypeWithInputs',
|
|
19
|
+
'StringTemplate',
|
|
20
|
+
'NgTemplateTemplate',
|
|
21
|
+
'ComponentTemplate',
|
|
22
|
+
'Props',
|
|
23
|
+
'PropsAttachedElements',
|
|
24
|
+
'PropsAttachedElementsInternal',
|
|
25
|
+
'PropsInternal',
|
|
26
|
+
'CreatePropsOptions',
|
|
27
|
+
'HostProps',
|
|
28
|
+
'PropHandlers',
|
|
29
|
+
'BindPropsOptions',
|
|
30
|
+
'UnbindPropsOptions',
|
|
31
|
+
].map((name) => [name, 'the props module']),
|
|
32
|
+
]);
|
|
33
|
+
const ET_PROPS_ATTRIBUTE = /\betProps\b/;
|
|
34
|
+
export default async function reportRemovedExports(tree) {
|
|
35
|
+
const findings = new Map();
|
|
36
|
+
function walkFiles(dir) {
|
|
37
|
+
for (const child of tree.children(dir)) {
|
|
38
|
+
if (child === 'node_modules')
|
|
39
|
+
continue;
|
|
40
|
+
const path = dir === '.' ? child : `${dir}/${child}`;
|
|
41
|
+
if (!tree.isFile(path)) {
|
|
42
|
+
walkFiles(path);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const messages = path.endsWith('.ts')
|
|
46
|
+
? findRemovedImports(tree, path)
|
|
47
|
+
: path.endsWith('.html')
|
|
48
|
+
? findEtPropsUsage(tree, path)
|
|
49
|
+
: [];
|
|
50
|
+
if (messages.length > 0)
|
|
51
|
+
findings.set(path, messages);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
walkFiles('.');
|
|
55
|
+
if (findings.size === 0)
|
|
56
|
+
return;
|
|
57
|
+
console.log('\nā ļø Manual migration required for the following files:');
|
|
58
|
+
findings.forEach((messages, file) => {
|
|
59
|
+
console.log(`\nš ${file}:`);
|
|
60
|
+
messages.forEach((message) => console.log(` - ${message}`));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function findRemovedImports(tree, filePath) {
|
|
64
|
+
const content = tree.read(filePath, 'utf-8');
|
|
65
|
+
if (!content || !content.includes('@ethlete/core'))
|
|
66
|
+
return [];
|
|
67
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
68
|
+
const messages = [];
|
|
69
|
+
for (const statement of sourceFile.statements) {
|
|
70
|
+
if (!ts.isImportDeclaration(statement) && !ts.isExportDeclaration(statement))
|
|
71
|
+
continue;
|
|
72
|
+
const specifier = statement.moduleSpecifier;
|
|
73
|
+
if (!specifier || !ts.isStringLiteral(specifier) || specifier.text !== '@ethlete/core')
|
|
74
|
+
continue;
|
|
75
|
+
const bindings = ts.isImportDeclaration(statement) ? statement.importClause?.namedBindings : statement.exportClause;
|
|
76
|
+
if (!bindings || ts.isNamespaceImport(bindings) || ts.isNamespaceExport(bindings))
|
|
77
|
+
continue;
|
|
78
|
+
for (const element of bindings.elements) {
|
|
79
|
+
const name = (element.propertyName ?? element.name).text;
|
|
80
|
+
const origin = REMOVED_EXPORTS.get(name);
|
|
81
|
+
if (origin) {
|
|
82
|
+
messages.push(`${name} was removed from @ethlete/core together with ${origin} and has no replacement.`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return messages;
|
|
87
|
+
}
|
|
88
|
+
function findEtPropsUsage(tree, filePath) {
|
|
89
|
+
const content = tree.read(filePath, 'utf-8');
|
|
90
|
+
if (!content || !ET_PROPS_ATTRIBUTE.test(content))
|
|
91
|
+
return [];
|
|
92
|
+
return ['[etProps] (PropsDirective) was removed from @ethlete/core together with the props module.'];
|
|
93
|
+
}
|
|
@@ -67,6 +67,8 @@ function findRouterStateServiceVariables(sourceFile) {
|
|
|
67
67
|
function findClassForRouterStateService(sourceFile, routerStateServiceVar) {
|
|
68
68
|
let classNode = null;
|
|
69
69
|
function visit(node) {
|
|
70
|
+
if (classNode)
|
|
71
|
+
return;
|
|
70
72
|
if (ts.isClassDeclaration(node)) {
|
|
71
73
|
const hasRouterStateService = node.members.some((member) => {
|
|
72
74
|
if (ts.isPropertyDeclaration(member) && ts.isIdentifier(member.name)) {
|
|
@@ -750,9 +752,10 @@ function addImportsToPackage(sourceFile, content, imports, packageName) {
|
|
|
750
752
|
}
|
|
751
753
|
});
|
|
752
754
|
if (existingImport?.importClause?.namedBindings && ts.isNamedImports(existingImport.importClause.namedBindings)) {
|
|
753
|
-
|
|
754
|
-
const
|
|
755
|
-
const
|
|
755
|
+
const existingElements = existingImport.importClause.namedBindings.elements;
|
|
756
|
+
const existingImportedNames = existingElements.map((el) => el.propertyName?.text ?? el.name.text);
|
|
757
|
+
const existingImports = existingElements.map((el) => el.getText(sourceFile));
|
|
758
|
+
const newImports = importsList.filter((imp) => !existingImportedNames.includes(imp));
|
|
756
759
|
if (newImports.length === 0)
|
|
757
760
|
return content;
|
|
758
761
|
const allImports = [...existingImports, ...newImports].sort();
|
|
@@ -782,7 +785,16 @@ function removeRouterStateServiceInjection(sourceFile, content, routerStateServi
|
|
|
782
785
|
node.members.forEach((member) => {
|
|
783
786
|
if (ts.isPropertyDeclaration(member) && ts.isIdentifier(member.name)) {
|
|
784
787
|
const memberName = member.name.text;
|
|
785
|
-
|
|
788
|
+
const injectsRouterState = member.initializer &&
|
|
789
|
+
ts.isCallExpression(member.initializer) &&
|
|
790
|
+
ts.isIdentifier(member.initializer.expression) &&
|
|
791
|
+
member.initializer.expression.text === 'inject' &&
|
|
792
|
+
member.initializer.arguments.some((argument) => ts.isIdentifier(argument) && argument.text === 'RouterStateService');
|
|
793
|
+
const hasRouterStateType = member.type &&
|
|
794
|
+
ts.isTypeReferenceNode(member.type) &&
|
|
795
|
+
ts.isIdentifier(member.type.typeName) &&
|
|
796
|
+
member.type.typeName.text === 'RouterStateService';
|
|
797
|
+
if (routerStateServiceVars.includes(memberName) && (injectsRouterState || hasRouterStateType)) {
|
|
786
798
|
const memberStart = member.getStart(sourceFile, true);
|
|
787
799
|
const memberEnd = member.getEnd();
|
|
788
800
|
let lineStart = memberStart;
|
|
@@ -807,7 +819,11 @@ function removeRouterStateServiceInjection(sourceFile, content, routerStateServi
|
|
|
807
819
|
member.parameters.forEach((param) => {
|
|
808
820
|
if (ts.isIdentifier(param.name)) {
|
|
809
821
|
const paramName = param.name.text;
|
|
810
|
-
|
|
822
|
+
const hasRouterStateType = param.type &&
|
|
823
|
+
ts.isTypeReferenceNode(param.type) &&
|
|
824
|
+
ts.isIdentifier(param.type.typeName) &&
|
|
825
|
+
param.type.typeName.text === 'RouterStateService';
|
|
826
|
+
if (routerStateServiceVars.includes(paramName) && hasRouterStateType) {
|
|
811
827
|
return;
|
|
812
828
|
}
|
|
813
829
|
}
|
|
@@ -951,11 +967,13 @@ export default async function migrateRouterStateService(tree) {
|
|
|
951
967
|
for (const child of children) {
|
|
952
968
|
const path = dir === '.' ? child : `${dir}/${child}`;
|
|
953
969
|
if (tree.isFile(path)) {
|
|
954
|
-
if (path.endsWith('.ts') && !path.includes('node_modules')
|
|
970
|
+
if (path.endsWith('.ts') && !path.includes('node_modules')) {
|
|
955
971
|
tsFiles.push(path);
|
|
956
972
|
}
|
|
957
973
|
}
|
|
958
974
|
else {
|
|
975
|
+
if (child === 'node_modules')
|
|
976
|
+
continue;
|
|
959
977
|
findFiles(path);
|
|
960
978
|
}
|
|
961
979
|
}
|
|
@@ -971,6 +989,14 @@ export default async function migrateRouterStateService(tree) {
|
|
|
971
989
|
continue;
|
|
972
990
|
console.log(`Processing: ${filePath}`);
|
|
973
991
|
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
992
|
+
const hasEthleteImport = sourceFile.statements.some((statement) => ts.isImportDeclaration(statement) &&
|
|
993
|
+
ts.isStringLiteral(statement.moduleSpecifier) &&
|
|
994
|
+
statement.moduleSpecifier.text.startsWith('@ethlete/') &&
|
|
995
|
+
!!statement.importClause?.namedBindings &&
|
|
996
|
+
ts.isNamedImports(statement.importClause.namedBindings) &&
|
|
997
|
+
statement.importClause.namedBindings.elements.some((element) => (element.propertyName?.text ?? element.name.text) === 'RouterStateService'));
|
|
998
|
+
if (!hasEthleteImport)
|
|
999
|
+
continue;
|
|
974
1000
|
const inlineResult = handleInlineInjectPatterns(sourceFile, content);
|
|
975
1001
|
let updatedContent = inlineResult.content;
|
|
976
1002
|
const updatedSourceFile = updatedContent !== content
|
|
@@ -1005,7 +1031,7 @@ export default async function migrateRouterStateService(tree) {
|
|
|
1005
1031
|
allImportsNeeded['@angular/core/rxjs-interop'].add('toObservable');
|
|
1006
1032
|
}
|
|
1007
1033
|
});
|
|
1008
|
-
for (const [original, replacement] of context.replacements) {
|
|
1034
|
+
for (const [original, replacement] of [...context.replacements].sort((a, b) => b[0].length - a[0].length)) {
|
|
1009
1035
|
if (replacement.includes('toObservable(')) {
|
|
1010
1036
|
allImportsNeeded['@angular/core/rxjs-interop'].add('toObservable');
|
|
1011
1037
|
}
|
|
@@ -1061,4 +1087,3 @@ export default async function migrateRouterStateService(tree) {
|
|
|
1061
1087
|
console.log('\nā¹ļø No RouterStateService usage found\n');
|
|
1062
1088
|
}
|
|
1063
1089
|
}
|
|
1064
|
-
//# sourceMappingURL=router-state-service.js.map
|
|
@@ -23,6 +23,16 @@
|
|
|
23
23
|
"type": "boolean",
|
|
24
24
|
"description": "Migrate router state service to v5",
|
|
25
25
|
"default": true
|
|
26
|
+
},
|
|
27
|
+
"migrateColorNaming": {
|
|
28
|
+
"type": "boolean",
|
|
29
|
+
"description": "Migrate theme naming to color naming (ProvideThemeDirective -> ProvideColorDirective, etc.)",
|
|
30
|
+
"default": true
|
|
31
|
+
},
|
|
32
|
+
"reportRemovedExports": {
|
|
33
|
+
"type": "boolean",
|
|
34
|
+
"description": "List imports of exports removed in v5 without a replacement (Memo, the props module)",
|
|
35
|
+
"default": true
|
|
26
36
|
}
|
|
27
37
|
},
|
|
28
38
|
"required": []
|