@ethlete/core 5.0.0-next.69 → 5.0.0-next.70
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 +8 -0
- package/fesm2022/ethlete-core.mjs +1 -5
- package/fesm2022/ethlete-core.mjs.map +1 -1
- package/generators/migrate-to-v5/router-state-service.js +2 -0
- package/generators/migrate-to-v5/sync-observable-consumers.js +80 -0
- package/generators/migrate-to-v5/viewport-service.js +2 -0
- package/migrations.json +1 -0
- package/package.json +2 -2
- package/types/ethlete-core.d.ts +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { logger } from '@nx/devkit';
|
|
2
2
|
import * as ts from 'typescript';
|
|
3
3
|
import { collectFiles } from './migration-files.js';
|
|
4
|
+
import { findSyncObservableConsumers } from './sync-observable-consumers.js';
|
|
4
5
|
import { applyReplacementsInClass, collectClasses, findServiceClasses, getServiceImport, isServiceParameter, isServiceProperty, isServiceStillUsed, removeServiceImport, } from './service-classes.js';
|
|
5
6
|
function getPropertyMaps() {
|
|
6
7
|
const signalPropertyMap = {
|
|
@@ -838,6 +839,7 @@ export default async function migrateRouterStateService(tree, scope) {
|
|
|
838
839
|
}
|
|
839
840
|
const sourceFileAfterCleanup = ts.createSourceFile(filePath, updatedContent, ts.ScriptTarget.Latest, true);
|
|
840
841
|
updatedContent = removeUnusedImports(sourceFileAfterCleanup, updatedContent);
|
|
842
|
+
review.push(...findSyncObservableConsumers(filePath, content, updatedContent));
|
|
841
843
|
if (updatedContent !== content) {
|
|
842
844
|
tree.write(filePath, updatedContent);
|
|
843
845
|
filesModified++;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
const MESSAGE = 'reads an observable made by toObservable(), which emits only after its first effect run. Read the signal directly or use computed().';
|
|
3
|
+
export function findSyncObservableConsumers(filePath, originalContent, content) {
|
|
4
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
5
|
+
const isAddedToObservable = (node) => ts.isCallExpression(node) &&
|
|
6
|
+
ts.isIdentifier(node.expression) &&
|
|
7
|
+
node.expression.text === 'toObservable' &&
|
|
8
|
+
!originalContent.includes(node.getText());
|
|
9
|
+
const containsAddedToObservable = (node) => isAddedToObservable(node) || (ts.forEachChild(node, containsAddedToObservable) ?? false);
|
|
10
|
+
const asyncNames = new Set();
|
|
11
|
+
const collectNames = (node) => {
|
|
12
|
+
if ((ts.isPropertyDeclaration(node) || ts.isVariableDeclaration(node)) &&
|
|
13
|
+
ts.isIdentifier(node.name) &&
|
|
14
|
+
node.initializer &&
|
|
15
|
+
containsAddedToObservable(node.initializer)) {
|
|
16
|
+
asyncNames.add(node.name.text);
|
|
17
|
+
}
|
|
18
|
+
ts.forEachChild(node, collectNames);
|
|
19
|
+
};
|
|
20
|
+
collectNames(sourceFile);
|
|
21
|
+
const isAsyncSource = (expression) => {
|
|
22
|
+
let root = expression;
|
|
23
|
+
while (ts.isCallExpression(root) && ts.isPropertyAccessExpression(root.expression)) {
|
|
24
|
+
root = root.expression.expression;
|
|
25
|
+
}
|
|
26
|
+
if (isAddedToObservable(root))
|
|
27
|
+
return true;
|
|
28
|
+
if (ts.isIdentifier(root))
|
|
29
|
+
return asyncNames.has(root.text);
|
|
30
|
+
return ts.isPropertyAccessExpression(root) && root.expression.kind === ts.SyntaxKind.ThisKeyword
|
|
31
|
+
? asyncNames.has(root.name.text)
|
|
32
|
+
: false;
|
|
33
|
+
};
|
|
34
|
+
const findings = [];
|
|
35
|
+
const visit = (node) => {
|
|
36
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
|
|
37
|
+
const [source, options] = node.arguments;
|
|
38
|
+
const name = node.expression.text;
|
|
39
|
+
if (name === 'toSignal' && source && options && requiresSync(options) && isAsyncSource(source)) {
|
|
40
|
+
findings.push({ node, label: `toSignal(${flatText(source)}, { requireSync: true })` });
|
|
41
|
+
}
|
|
42
|
+
if (name === 'withLatestFrom') {
|
|
43
|
+
for (const argument of node.arguments.filter(isAsyncSource)) {
|
|
44
|
+
findings.push({ node: argument, label: `withLatestFrom(${flatText(argument)})` });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (ts.isCallExpression(node) &&
|
|
49
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
50
|
+
node.expression.name.text === 'pipe' &&
|
|
51
|
+
isAsyncSource(node.expression.expression)) {
|
|
52
|
+
for (const operator of node.arguments.filter(isFirstValueOperator)) {
|
|
53
|
+
findings.push({ node: operator, label: flatText(operator) });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
ts.forEachChild(node, visit);
|
|
57
|
+
};
|
|
58
|
+
visit(sourceFile);
|
|
59
|
+
return findings
|
|
60
|
+
.sort((a, b) => a.node.getStart() - b.node.getStart())
|
|
61
|
+
.map(({ node, label }) => {
|
|
62
|
+
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
63
|
+
return `${filePath}:${line}: ${label} ${MESSAGE}`;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function requiresSync(options) {
|
|
67
|
+
return (ts.isObjectLiteralExpression(options) &&
|
|
68
|
+
options.properties.some((property) => ts.isPropertyAssignment(property) &&
|
|
69
|
+
property.name.getText() === 'requireSync' &&
|
|
70
|
+
property.initializer.kind === ts.SyntaxKind.TrueKeyword));
|
|
71
|
+
}
|
|
72
|
+
function isFirstValueOperator(operator) {
|
|
73
|
+
if (!ts.isCallExpression(operator) || !ts.isIdentifier(operator.expression))
|
|
74
|
+
return false;
|
|
75
|
+
const name = operator.expression.text;
|
|
76
|
+
return name === 'first' || (name === 'take' && operator.arguments[0]?.getText() === '1');
|
|
77
|
+
}
|
|
78
|
+
function flatText(node) {
|
|
79
|
+
return node.getText().replace(/\s+/g, ' ');
|
|
80
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { logger } from '@nx/devkit';
|
|
2
2
|
import * as ts from 'typescript';
|
|
3
3
|
import { collectFiles } from './migration-files.js';
|
|
4
|
+
import { findSyncObservableConsumers } from './sync-observable-consumers.js';
|
|
4
5
|
import { applyReplacementsInClass, collectClasses, findServiceClasses, getServiceImport, isServiceParameter, isServiceProperty, isServiceStillUsed, removeServiceImport, } from './service-classes.js';
|
|
5
6
|
export default async function migrateViewportService(tree, scope) {
|
|
6
7
|
const tsFiles = collectFiles(tree, scope, ['.ts']);
|
|
@@ -91,6 +92,7 @@ export default async function migrateViewportService(tree, scope) {
|
|
|
91
92
|
}
|
|
92
93
|
const sourceFileAfterCleanup = ts.createSourceFile(filePath, updatedContent, ts.ScriptTarget.Latest, true);
|
|
93
94
|
updatedContent = removeUnusedImports(sourceFileAfterCleanup, updatedContent);
|
|
95
|
+
review.push(...findSyncObservableConsumers(filePath, content, updatedContent));
|
|
94
96
|
const sourceFileForTemplates = ts.createSourceFile(filePath, updatedContent, ts.ScriptTarget.Latest, true);
|
|
95
97
|
const originalClasses = collectClasses(sourceFile);
|
|
96
98
|
const inlineTemplateEdits = [];
|
package/migrations.json
CHANGED
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"level": "required",
|
|
34
34
|
"description": "Rewrite surface theme interactionColor maps into the { color, onColor, inkColor } swatch shape.",
|
|
35
35
|
"generator": "@ethlete/core:migrate-surface-interaction-swatch",
|
|
36
|
+
"requires": ["ts-morph"],
|
|
36
37
|
"docs": "/core/theming"
|
|
37
38
|
},
|
|
38
39
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ethlete/core",
|
|
3
|
-
"version": "5.0.0-next.
|
|
3
|
+
"version": "5.0.0-next.70",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/ethlete-io/ethdk",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@angular/router": "^22.1.0",
|
|
27
27
|
"@ethlete/types": "^2.0.0-beta.4",
|
|
28
28
|
"@floating-ui/dom": "^1.6.0",
|
|
29
|
-
"@nx/devkit": "
|
|
29
|
+
"@nx/devkit": ">=23.0.0",
|
|
30
30
|
"rxjs": "^7.8.0",
|
|
31
31
|
"ts-morph": "^28.0.0",
|
|
32
32
|
"typescript": "^6.0.0"
|
package/types/ethlete-core.d.ts
CHANGED
|
@@ -4012,5 +4012,5 @@ export type UnsavedChangesGuardRef<T> = UnsavedChangesTrackerRef<T> & {
|
|
|
4012
4012
|
*/
|
|
4013
4013
|
export declare const createUnsavedChangesGuard: <T>(config: CreateUnsavedChangesGuardConfig<T>) => UnsavedChangesGuardRef<T>;
|
|
4014
4014
|
/** The version of `@ethlete/core` this build was cut from. */
|
|
4015
|
-
export declare const CORE_VERSION = "5.0.0-next.
|
|
4015
|
+
export declare const CORE_VERSION = "5.0.0-next.70";
|
|
4016
4016
|
export { json_ld_d_exports as JsonLD, type OverlayRuntimeRef, type OverlayRuntimeState };
|