@esportsplus/reactivity 0.28.0 → 0.28.2
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/build/compiler/array.js +23 -2
- package/build/compiler/index.js +53 -8
- package/package.json +1 -1
- package/src/compiler/array.ts +31 -2
- package/src/compiler/index.ts +79 -11
package/build/compiler/array.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
2
|
import { ast } from '@esportsplus/typescript/compiler';
|
|
3
3
|
import { COMPILER_NAMESPACE, COMPILER_TYPES } from '../constants.js';
|
|
4
|
+
function getElementTypeText(typeNode, sourceFile) {
|
|
5
|
+
if (ts.isArrayTypeNode(typeNode)) {
|
|
6
|
+
return typeNode.elementType.getText(sourceFile);
|
|
7
|
+
}
|
|
8
|
+
if (ts.isTypeReferenceNode(typeNode) &&
|
|
9
|
+
ts.isIdentifier(typeNode.typeName) &&
|
|
10
|
+
typeNode.typeName.text === 'Array' &&
|
|
11
|
+
typeNode.typeArguments &&
|
|
12
|
+
typeNode.typeArguments.length > 0) {
|
|
13
|
+
return typeNode.typeArguments[0].getText(sourceFile);
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
4
17
|
function isReactiveCall(node) {
|
|
5
18
|
return ts.isIdentifier(node.expression) && node.expression.text === 'reactive';
|
|
6
19
|
}
|
|
@@ -8,14 +21,22 @@ function visit(ctx, node) {
|
|
|
8
21
|
if (ts.isCallExpression(node) && isReactiveCall(node) && node.arguments.length > 0) {
|
|
9
22
|
let arg = node.arguments[0], expression = ts.isAsExpression(arg) ? arg.expression : arg;
|
|
10
23
|
if (ts.isArrayLiteralExpression(expression)) {
|
|
24
|
+
let elementType = null;
|
|
25
|
+
if (ts.isAsExpression(arg) && arg.type) {
|
|
26
|
+
elementType = getElementTypeText(arg.type, ctx.sourceFile);
|
|
27
|
+
}
|
|
28
|
+
else if (node.parent && ts.isVariableDeclaration(node.parent) && node.parent.type) {
|
|
29
|
+
elementType = getElementTypeText(node.parent.type, ctx.sourceFile);
|
|
30
|
+
}
|
|
11
31
|
if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
12
32
|
ctx.bindings.set(node.parent.name.text, COMPILER_TYPES.Array);
|
|
13
33
|
}
|
|
34
|
+
let typeParam = elementType ? `<${elementType}>` : '';
|
|
14
35
|
ctx.replacements.push({
|
|
15
36
|
node,
|
|
16
37
|
generate: (sf) => expression.elements.length > 0
|
|
17
|
-
? ` new ${COMPILER_NAMESPACE}.ReactiveArray(...${expression.getText(sf)})`
|
|
18
|
-
: ` new ${COMPILER_NAMESPACE}.ReactiveArray()`
|
|
38
|
+
? ` new ${COMPILER_NAMESPACE}.ReactiveArray${typeParam}(...${expression.getText(sf)})`
|
|
39
|
+
: ` new ${COMPILER_NAMESPACE}.ReactiveArray${typeParam}()`
|
|
19
40
|
});
|
|
20
41
|
}
|
|
21
42
|
}
|
package/build/compiler/index.js
CHANGED
|
@@ -1,13 +1,50 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
import {
|
|
2
|
+
import { imports } from '@esportsplus/typescript/compiler';
|
|
3
3
|
import { COMPILER_ENTRYPOINT, COMPILER_NAMESPACE, PACKAGE } from '../constants.js';
|
|
4
4
|
import array from './array.js';
|
|
5
5
|
import object from './object.js';
|
|
6
6
|
function hasReactiveImport(sourceFile) {
|
|
7
7
|
return imports.find(sourceFile, PACKAGE).some(i => i.specifiers.has(COMPILER_ENTRYPOINT));
|
|
8
8
|
}
|
|
9
|
-
function
|
|
10
|
-
|
|
9
|
+
function isReactiveSymbol(checker, node) {
|
|
10
|
+
let symbol = checker.getSymbolAtLocation(node);
|
|
11
|
+
if (!symbol) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
15
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
16
|
+
}
|
|
17
|
+
let declarations = symbol.getDeclarations();
|
|
18
|
+
if (!declarations || declarations.length === 0) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
for (let i = 0, n = declarations.length; i < n; i++) {
|
|
22
|
+
let decl = declarations[i], sourceFile = decl.getSourceFile();
|
|
23
|
+
if (sourceFile.fileName.includes(PACKAGE) || sourceFile.fileName.includes('reactivity')) {
|
|
24
|
+
if (symbol.name === COMPILER_ENTRYPOINT) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
function isReactiveCallExpression(checker, node) {
|
|
32
|
+
if (!ts.isCallExpression(node)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
let expr = node.expression;
|
|
36
|
+
if (ts.isIdentifier(expr)) {
|
|
37
|
+
if (expr.text === COMPILER_ENTRYPOINT) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (checker) {
|
|
41
|
+
return isReactiveSymbol(checker, expr);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (ts.isPropertyAccessExpression(expr) && expr.name.text === COMPILER_ENTRYPOINT && checker) {
|
|
45
|
+
return isReactiveSymbol(checker, expr);
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
11
48
|
}
|
|
12
49
|
const plugin = {
|
|
13
50
|
patterns: ['reactive(', 'reactive<'],
|
|
@@ -21,15 +58,23 @@ const plugin = {
|
|
|
21
58
|
replacements.push(...objectResult.replacements);
|
|
22
59
|
let arrayResult = array(ctx.sourceFile, bindings, ctx.checker);
|
|
23
60
|
replacements.push(...arrayResult);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
|
|
61
|
+
let transformedNodes = new Set(replacements.map(r => r.node));
|
|
62
|
+
function findRemainingReactiveCalls(node) {
|
|
63
|
+
if (isReactiveCallExpression(ctx.checker, node) && !transformedNodes.has(node)) {
|
|
64
|
+
let call = node;
|
|
65
|
+
replacements.push({
|
|
66
|
+
generate: () => `${COMPILER_NAMESPACE}.reactive(${call.arguments.map(a => a.getText(ctx.sourceFile)).join(', ')})`,
|
|
67
|
+
node: call
|
|
68
|
+
});
|
|
28
69
|
}
|
|
70
|
+
ts.forEachChild(node, findRemainingReactiveCalls);
|
|
71
|
+
}
|
|
72
|
+
findRemainingReactiveCalls(ctx.sourceFile);
|
|
73
|
+
if (replacements.length > 0 || prepend.length > 0) {
|
|
29
74
|
importsIntent.push({
|
|
30
75
|
namespace: COMPILER_NAMESPACE,
|
|
31
76
|
package: PACKAGE,
|
|
32
|
-
remove
|
|
77
|
+
remove: [COMPILER_ENTRYPOINT]
|
|
33
78
|
});
|
|
34
79
|
}
|
|
35
80
|
return {
|
package/package.json
CHANGED
package/src/compiler/array.ts
CHANGED
|
@@ -13,6 +13,24 @@ interface VisitContext {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
function getElementTypeText(typeNode: ts.TypeNode, sourceFile: ts.SourceFile): string | null {
|
|
17
|
+
if (ts.isArrayTypeNode(typeNode)) {
|
|
18
|
+
return typeNode.elementType.getText(sourceFile);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (
|
|
22
|
+
ts.isTypeReferenceNode(typeNode) &&
|
|
23
|
+
ts.isIdentifier(typeNode.typeName) &&
|
|
24
|
+
typeNode.typeName.text === 'Array' &&
|
|
25
|
+
typeNode.typeArguments &&
|
|
26
|
+
typeNode.typeArguments.length > 0
|
|
27
|
+
) {
|
|
28
|
+
return typeNode.typeArguments[0].getText(sourceFile);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
16
34
|
function isReactiveCall(node: ts.CallExpression): boolean {
|
|
17
35
|
return ts.isIdentifier(node.expression) && node.expression.text === 'reactive';
|
|
18
36
|
}
|
|
@@ -23,15 +41,26 @@ function visit(ctx: VisitContext, node: ts.Node): void {
|
|
|
23
41
|
expression = ts.isAsExpression(arg) ? arg.expression : arg;
|
|
24
42
|
|
|
25
43
|
if (ts.isArrayLiteralExpression(expression)) {
|
|
44
|
+
let elementType: string | null = null;
|
|
45
|
+
|
|
46
|
+
if (ts.isAsExpression(arg) && arg.type) {
|
|
47
|
+
elementType = getElementTypeText(arg.type, ctx.sourceFile);
|
|
48
|
+
}
|
|
49
|
+
else if (node.parent && ts.isVariableDeclaration(node.parent) && node.parent.type) {
|
|
50
|
+
elementType = getElementTypeText(node.parent.type, ctx.sourceFile);
|
|
51
|
+
}
|
|
52
|
+
|
|
26
53
|
if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
27
54
|
ctx.bindings.set(node.parent.name.text, COMPILER_TYPES.Array);
|
|
28
55
|
}
|
|
29
56
|
|
|
57
|
+
let typeParam = elementType ? `<${elementType}>` : '';
|
|
58
|
+
|
|
30
59
|
ctx.replacements.push({
|
|
31
60
|
node,
|
|
32
61
|
generate: (sf) => expression.elements.length > 0
|
|
33
|
-
? ` new ${COMPILER_NAMESPACE}.ReactiveArray(...${expression.getText(sf)})`
|
|
34
|
-
: ` new ${COMPILER_NAMESPACE}.ReactiveArray()`
|
|
62
|
+
? ` new ${COMPILER_NAMESPACE}.ReactiveArray${typeParam}(...${expression.getText(sf)})`
|
|
63
|
+
: ` new ${COMPILER_NAMESPACE}.ReactiveArray${typeParam}()`
|
|
35
64
|
});
|
|
36
65
|
}
|
|
37
66
|
}
|
package/src/compiler/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ImportIntent, Plugin, ReplacementIntent, TransformContext } from '@esportsplus/typescript/compiler';
|
|
2
2
|
import { ts } from '@esportsplus/typescript';
|
|
3
|
-
import {
|
|
3
|
+
import { imports } from '@esportsplus/typescript/compiler';
|
|
4
4
|
import { COMPILER_ENTRYPOINT, COMPILER_NAMESPACE, PACKAGE } from '~/constants';
|
|
5
5
|
import type { Bindings } from '~/types';
|
|
6
6
|
import array from './array';
|
|
@@ -11,8 +11,66 @@ function hasReactiveImport(sourceFile: ts.SourceFile): boolean {
|
|
|
11
11
|
return imports.find(sourceFile, PACKAGE).some(i => i.specifiers.has(COMPILER_ENTRYPOINT));
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
function
|
|
15
|
-
|
|
14
|
+
function isReactiveSymbol(checker: ts.TypeChecker, node: ts.Node): boolean {
|
|
15
|
+
let symbol = checker.getSymbolAtLocation(node);
|
|
16
|
+
|
|
17
|
+
if (!symbol) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Follow aliases to original symbol
|
|
22
|
+
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
23
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let declarations = symbol.getDeclarations();
|
|
27
|
+
|
|
28
|
+
if (!declarations || declarations.length === 0) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (let i = 0, n = declarations.length; i < n; i++) {
|
|
33
|
+
let decl = declarations[i],
|
|
34
|
+
sourceFile = decl.getSourceFile();
|
|
35
|
+
|
|
36
|
+
// Check if declaration is from our package
|
|
37
|
+
if (sourceFile.fileName.includes(PACKAGE) || sourceFile.fileName.includes('reactivity')) {
|
|
38
|
+
// Verify it's the reactive export
|
|
39
|
+
if (symbol.name === COMPILER_ENTRYPOINT) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isReactiveCallExpression(checker: ts.TypeChecker | undefined, node: ts.Node): node is ts.CallExpression {
|
|
49
|
+
if (!ts.isCallExpression(node)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let expr = node.expression;
|
|
54
|
+
|
|
55
|
+
// Direct call: reactive(...) or aliasedName(...)
|
|
56
|
+
if (ts.isIdentifier(expr)) {
|
|
57
|
+
// Fast path: literal "reactive"
|
|
58
|
+
if (expr.text === COMPILER_ENTRYPOINT) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Use checker to resolve aliases
|
|
63
|
+
if (checker) {
|
|
64
|
+
return isReactiveSymbol(checker, expr);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Property access: ns.reactive(...)
|
|
69
|
+
if (ts.isPropertyAccessExpression(expr) && expr.name.text === COMPILER_ENTRYPOINT && checker) {
|
|
70
|
+
return isReactiveSymbol(checker, expr);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return false;
|
|
16
74
|
}
|
|
17
75
|
|
|
18
76
|
|
|
@@ -40,20 +98,30 @@ const plugin: Plugin = {
|
|
|
40
98
|
|
|
41
99
|
replacements.push(...arrayResult);
|
|
42
100
|
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
let remove: string[] = [];
|
|
101
|
+
// Find remaining reactive() calls that weren't transformed and replace with namespace version
|
|
102
|
+
let transformedNodes = new Set(replacements.map(r => r.node));
|
|
46
103
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
104
|
+
function findRemainingReactiveCalls(node: ts.Node): void {
|
|
105
|
+
if (isReactiveCallExpression(ctx.checker, node) && !transformedNodes.has(node)) {
|
|
106
|
+
let call = node;
|
|
107
|
+
|
|
108
|
+
replacements.push({
|
|
109
|
+
generate: () => `${COMPILER_NAMESPACE}.reactive(${call.arguments.map(a => a.getText(ctx.sourceFile)).join(', ')})`,
|
|
110
|
+
node: call
|
|
111
|
+
});
|
|
51
112
|
}
|
|
52
113
|
|
|
114
|
+
ts.forEachChild(node, findRemainingReactiveCalls);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
findRemainingReactiveCalls(ctx.sourceFile);
|
|
118
|
+
|
|
119
|
+
// Build import intent
|
|
120
|
+
if (replacements.length > 0 || prepend.length > 0) {
|
|
53
121
|
importsIntent.push({
|
|
54
122
|
namespace: COMPILER_NAMESPACE,
|
|
55
123
|
package: PACKAGE,
|
|
56
|
-
remove
|
|
124
|
+
remove: [COMPILER_ENTRYPOINT]
|
|
57
125
|
});
|
|
58
126
|
}
|
|
59
127
|
|