@esportsplus/reactivity 0.37.0 → 0.37.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/bench/cellx.bench.ts +4 -4
- package/bench/dynamic.bench.ts +10 -9
- package/bench/kairo.bench.ts +28 -33
- package/bench/molbench.bench.ts +4 -4
- package/bench/reactive/array.bench.ts +163 -163
- package/bench/reactive/reactive-object.bench.ts +47 -45
- package/bench/system.bench.ts +263 -263
- package/build/compiler/array.js +68 -73
- package/build/compiler/bindings.d.ts +19 -0
- package/build/compiler/bindings.js +65 -0
- package/build/compiler/index.js +15 -6
- package/build/compiler/object.js +10 -6
- package/build/compiler/primitives.js +137 -138
- package/build/compiler/types.d.ts +7 -2
- package/build/reactive/array.js +19 -12
- package/build/system.js +1 -1
- package/package.json +3 -3
- package/pnpm-workspace.yaml +2 -4
- package/src/compiler/array.ts +86 -89
- package/src/compiler/bindings.ts +100 -0
- package/src/compiler/index.ts +22 -9
- package/src/compiler/object.ts +12 -7
- package/src/compiler/primitives.ts +190 -187
- package/src/compiler/types.ts +7 -2
- package/src/reactive/array.ts +27 -14
- package/src/system.ts +3 -1
- package/test/async-iterable.test.ts +42 -0
- package/test/compiler/compiler.test.ts +115 -45
- package/test/reactive/array.test.ts +54 -14
- package/vitest.config.ts +2 -2
package/build/compiler/array.js
CHANGED
|
@@ -1,20 +1,7 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
import { ast } from '@esportsplus/typescript/compiler';
|
|
3
2
|
import { COMPOUND_OPERATORS, NAMESPACE, TYPES } from './constants.js';
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
}
|
|
17
|
-
function visit(ctx, node) {
|
|
3
|
+
import scope from './bindings.js';
|
|
4
|
+
function declare(ctx, node) {
|
|
18
5
|
if (ctx.isReactiveCall(node) && node.arguments.length > 0) {
|
|
19
6
|
let arg = node.arguments[0], expression = ts.isAsExpression(arg) ? arg.expression : arg;
|
|
20
7
|
if (ts.isArrayLiteralExpression(expression)) {
|
|
@@ -26,7 +13,7 @@ function visit(ctx, node) {
|
|
|
26
13
|
elementType = getElementTypeText(node.parent.type, ctx.sourceFile);
|
|
27
14
|
}
|
|
28
15
|
if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
29
|
-
ctx.bindings
|
|
16
|
+
scope.declare(ctx.bindings, node.parent.name, TYPES.Array);
|
|
30
17
|
}
|
|
31
18
|
let typeParam = elementType ? `<${elementType}>` : '';
|
|
32
19
|
ctx.replacements.push({
|
|
@@ -37,100 +24,107 @@ function visit(ctx, node) {
|
|
|
37
24
|
});
|
|
38
25
|
}
|
|
39
26
|
}
|
|
40
|
-
if (ts.isVariableDeclaration(node) &&
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (path && ctx.bindings.get(path) === TYPES.Array) {
|
|
47
|
-
ctx.bindings.set(node.name.text, TYPES.Array);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
27
|
+
if (ts.isVariableDeclaration(node) &&
|
|
28
|
+
ts.isIdentifier(node.name) &&
|
|
29
|
+
node.initializer &&
|
|
30
|
+
(ts.isIdentifier(node.initializer) || ts.isPropertyAccessExpression(node.initializer)) &&
|
|
31
|
+
scope.isArray(ctx.bindings, node.initializer)) {
|
|
32
|
+
scope.declare(ctx.bindings, node.name, TYPES.Array);
|
|
50
33
|
}
|
|
51
34
|
if ((ts.isFunctionDeclaration(node) || ts.isArrowFunction(node)) && node.parameters) {
|
|
52
35
|
for (let i = 0, n = node.parameters.length; i < n; i++) {
|
|
53
36
|
let param = node.parameters[i];
|
|
54
|
-
if (
|
|
37
|
+
if (ts.isIdentifier(param.name) &&
|
|
38
|
+
param.type &&
|
|
55
39
|
ts.isTypeReferenceNode(param.type) &&
|
|
56
40
|
ts.isIdentifier(param.type.typeName) &&
|
|
57
41
|
param.type.typeName.text === 'ReactiveArray') {
|
|
58
|
-
ctx.bindings
|
|
42
|
+
scope.declare(ctx.bindings, param.name, TYPES.Array);
|
|
59
43
|
}
|
|
60
44
|
}
|
|
61
45
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
else if (parent && ts.isPrefixUnaryExpression(parent)) {
|
|
89
|
-
let op = parent.operator === ts.SyntaxKind.PlusPlusToken ? '+' : '-';
|
|
46
|
+
node.forEachChild(child => declare(ctx, child));
|
|
47
|
+
}
|
|
48
|
+
function getElementTypeText(typeNode, sourceFile) {
|
|
49
|
+
if (ts.isArrayTypeNode(typeNode)) {
|
|
50
|
+
return typeNode.elementType.getText(sourceFile);
|
|
51
|
+
}
|
|
52
|
+
if (ts.isTypeReferenceNode(typeNode) &&
|
|
53
|
+
ts.isIdentifier(typeNode.typeName) &&
|
|
54
|
+
typeNode.typeName.text === 'Array' &&
|
|
55
|
+
typeNode.typeArguments &&
|
|
56
|
+
typeNode.typeArguments.length > 0) {
|
|
57
|
+
return typeNode.typeArguments[0].getText(sourceFile);
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function rewrite(ctx, node) {
|
|
62
|
+
if (ts.isTypeNode(node)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (ts.isPropertyAccessExpression(node) && node.name.text === 'length' && scope.isArray(ctx.bindings, node.expression)) {
|
|
66
|
+
let expr = node.expression, parent = node.parent;
|
|
67
|
+
if (parent && ts.isBinaryExpression(parent) && parent.left === node && (parent.operatorToken.kind === ts.SyntaxKind.EqualsToken || COMPOUND_OPERATORS.has(parent.operatorToken.kind))) {
|
|
68
|
+
let op = COMPOUND_OPERATORS.get(parent.operatorToken.kind);
|
|
69
|
+
if (op === undefined) {
|
|
90
70
|
ctx.replacements.push({
|
|
91
71
|
node: parent,
|
|
92
|
-
generate: (sf) => `${expr.getText(sf)}.$length = ${
|
|
72
|
+
generate: (sf) => `${expr.getText(sf)}.$length = ${parent.right.getText(sf)}`
|
|
93
73
|
});
|
|
94
74
|
}
|
|
95
75
|
else {
|
|
96
76
|
ctx.replacements.push({
|
|
97
|
-
node,
|
|
98
|
-
generate: (sf) => `${expr.getText(sf)}.$length`
|
|
77
|
+
node: parent,
|
|
78
|
+
generate: (sf) => `${expr.getText(sf)}.$length = ${expr.getText(sf)}.length ${op} ${parent.right.getText(sf)}`
|
|
99
79
|
});
|
|
100
80
|
}
|
|
101
81
|
}
|
|
82
|
+
else if (parent && (ts.isPostfixUnaryExpression(parent) || ts.isPrefixUnaryExpression(parent))) {
|
|
83
|
+
let op = parent.operator === ts.SyntaxKind.PlusPlusToken ? '+' : '-';
|
|
84
|
+
ctx.replacements.push({
|
|
85
|
+
node: parent,
|
|
86
|
+
generate: (sf) => `${expr.getText(sf)}.$length = ${expr.getText(sf)}.length ${op} 1`
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
ctx.replacements.push({
|
|
91
|
+
node,
|
|
92
|
+
generate: (sf) => `${expr.getText(sf)}.$length`
|
|
93
|
+
});
|
|
94
|
+
}
|
|
102
95
|
}
|
|
103
96
|
if (ts.isBinaryExpression(node) &&
|
|
104
97
|
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
105
|
-
ts.isElementAccessExpression(node.left)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
98
|
+
ts.isElementAccessExpression(node.left) &&
|
|
99
|
+
scope.isArray(ctx.bindings, node.left.expression)) {
|
|
100
|
+
let element = node.left;
|
|
101
|
+
ctx.replacements.push({
|
|
102
|
+
node,
|
|
103
|
+
generate: (sf) => `${element.expression.getText(sf)}.$set(
|
|
111
104
|
${element.argumentExpression.getText(sf)},
|
|
112
105
|
${node.right.getText(sf)}
|
|
113
106
|
)`
|
|
114
|
-
|
|
115
|
-
}
|
|
107
|
+
});
|
|
116
108
|
}
|
|
117
109
|
if (ts.isBinaryExpression(node) &&
|
|
118
110
|
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
119
|
-
ts.isIdentifier(node.left)
|
|
111
|
+
ts.isIdentifier(node.left) &&
|
|
112
|
+
scope.isArray(ctx.bindings, node.left)) {
|
|
120
113
|
let name = node.left.text, right = node.right;
|
|
121
114
|
while (ts.isAsExpression(right) || ts.isTypeAssertion(right)) {
|
|
122
115
|
right = right.expression;
|
|
123
116
|
}
|
|
124
|
-
if (
|
|
117
|
+
if (ts.isArrayLiteralExpression(right)) {
|
|
118
|
+
let elements = right;
|
|
125
119
|
ctx.replacements.push({
|
|
126
120
|
node,
|
|
127
|
-
generate: (sf) =>
|
|
128
|
-
? `${name}.splice(0, ${name}.length, ...${
|
|
121
|
+
generate: (sf) => elements.elements.length > 0
|
|
122
|
+
? `${name}.splice(0, ${name}.length, ...${elements.getText(sf)})`
|
|
129
123
|
: `${name}.splice(0, ${name}.length)`
|
|
130
124
|
});
|
|
131
125
|
}
|
|
132
126
|
}
|
|
133
|
-
node.forEachChild(
|
|
127
|
+
node.forEachChild(child => rewrite(ctx, child));
|
|
134
128
|
}
|
|
135
129
|
export default (sourceFile, bindings, isReactiveCall) => {
|
|
136
130
|
let ctx = {
|
|
@@ -139,6 +133,7 @@ export default (sourceFile, bindings, isReactiveCall) => {
|
|
|
139
133
|
replacements: [],
|
|
140
134
|
sourceFile
|
|
141
135
|
};
|
|
142
|
-
|
|
136
|
+
declare(ctx, sourceFile);
|
|
137
|
+
rewrite(ctx, sourceFile);
|
|
143
138
|
return ctx.replacements;
|
|
144
139
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ts } from '@esportsplus/typescript';
|
|
2
|
+
import { TYPES } from './constants.js';
|
|
3
|
+
import type { Bindings } from './types.js';
|
|
4
|
+
declare const create: (checker: ts.Checker) => Bindings;
|
|
5
|
+
declare const declare: (bindings: Bindings, identifier: ts.Identifier, type: TYPES) => void;
|
|
6
|
+
declare const declarePath: (bindings: Bindings, identifier: ts.Identifier, path: string) => void;
|
|
7
|
+
declare const isArray: (bindings: Bindings, expression: ts.Expression) => boolean;
|
|
8
|
+
declare const resolve: (bindings: Bindings, identifier: ts.Identifier) => {
|
|
9
|
+
symbol: ts.Symbol;
|
|
10
|
+
type: TYPES;
|
|
11
|
+
} | null;
|
|
12
|
+
declare const _default: {
|
|
13
|
+
create: typeof create;
|
|
14
|
+
declare: typeof declare;
|
|
15
|
+
declarePath: typeof declarePath;
|
|
16
|
+
isArray: typeof isArray;
|
|
17
|
+
resolve: typeof resolve;
|
|
18
|
+
};
|
|
19
|
+
export default _default;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ts } from '@esportsplus/typescript';
|
|
2
|
+
import { TYPES } from './constants.js';
|
|
3
|
+
function root(expression) {
|
|
4
|
+
let parts = [];
|
|
5
|
+
while (ts.isPropertyAccessExpression(expression)) {
|
|
6
|
+
parts.push(expression.name.text);
|
|
7
|
+
expression = expression.expression;
|
|
8
|
+
}
|
|
9
|
+
if (!ts.isIdentifier(expression) || parts.length === 0) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return { path: parts.reverse().join('.'), root: expression };
|
|
13
|
+
}
|
|
14
|
+
function symbolOf(bindings, identifier) {
|
|
15
|
+
let parent = identifier.parent;
|
|
16
|
+
if (parent && ts.isShorthandPropertyAssignment(parent) && parent.name === identifier) {
|
|
17
|
+
return bindings.checker.getShorthandAssignmentValueSymbol(parent);
|
|
18
|
+
}
|
|
19
|
+
return bindings.checker.getSymbolAtLocation(identifier);
|
|
20
|
+
}
|
|
21
|
+
const create = (checker) => ({
|
|
22
|
+
checker,
|
|
23
|
+
names: new Set(),
|
|
24
|
+
paths: new Map(),
|
|
25
|
+
symbols: new Map()
|
|
26
|
+
});
|
|
27
|
+
const declare = (bindings, identifier, type) => {
|
|
28
|
+
let symbol = bindings.checker.getSymbolAtLocation(identifier);
|
|
29
|
+
if (symbol) {
|
|
30
|
+
bindings.names.add(identifier.text);
|
|
31
|
+
bindings.symbols.set(symbol, type);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const declarePath = (bindings, identifier, path) => {
|
|
35
|
+
let symbol = bindings.checker.getSymbolAtLocation(identifier);
|
|
36
|
+
if (!symbol) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
let paths = bindings.paths.get(symbol);
|
|
40
|
+
if (!paths) {
|
|
41
|
+
paths = new Set();
|
|
42
|
+
bindings.paths.set(symbol, paths);
|
|
43
|
+
}
|
|
44
|
+
bindings.names.add(identifier.text);
|
|
45
|
+
paths.add(path);
|
|
46
|
+
};
|
|
47
|
+
const isArray = (bindings, expression) => {
|
|
48
|
+
if (ts.isIdentifier(expression)) {
|
|
49
|
+
return resolve(bindings, expression)?.type === TYPES.Array;
|
|
50
|
+
}
|
|
51
|
+
let access = root(expression);
|
|
52
|
+
if (!access || !bindings.names.has(access.root.text)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
let symbol = symbolOf(bindings, access.root);
|
|
56
|
+
return symbol !== undefined && bindings.paths.get(symbol)?.has(access.path) === true;
|
|
57
|
+
};
|
|
58
|
+
const resolve = (bindings, identifier) => {
|
|
59
|
+
if (!bindings.names.has(identifier.text)) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
let symbol = symbolOf(bindings, identifier), type = symbol && bindings.symbols.get(symbol);
|
|
63
|
+
return symbol && type !== undefined ? { symbol, type } : null;
|
|
64
|
+
};
|
|
65
|
+
export default { create, declare, declarePath, isArray, resolve };
|
package/build/compiler/index.js
CHANGED
|
@@ -4,6 +4,13 @@ import { ENTRYPOINT, NAMESPACE, PACKAGE_NAME } from './constants.js';
|
|
|
4
4
|
import array from './array.js';
|
|
5
5
|
import object from './object.js';
|
|
6
6
|
import primitives from './primitives.js';
|
|
7
|
+
import scope from './bindings.js';
|
|
8
|
+
function collect(checker, node, calls) {
|
|
9
|
+
if (isReactiveCallExpression(checker, node)) {
|
|
10
|
+
calls.add(node);
|
|
11
|
+
}
|
|
12
|
+
node.forEachChild(child => collect(checker, child, calls));
|
|
13
|
+
}
|
|
7
14
|
function isReactiveCallExpression(checker, node) {
|
|
8
15
|
if (!ts.isCallExpression(node)) {
|
|
9
16
|
return false;
|
|
@@ -12,8 +19,8 @@ function isReactiveCallExpression(checker, node) {
|
|
|
12
19
|
if (ts.isIdentifier(expr)) {
|
|
13
20
|
return imports.includes(checker, expr, PACKAGE_NAME, ENTRYPOINT);
|
|
14
21
|
}
|
|
15
|
-
if (ts.isPropertyAccessExpression(expr) && expr.name.text === ENTRYPOINT) {
|
|
16
|
-
return imports.includes(checker, expr, PACKAGE_NAME);
|
|
22
|
+
if (ts.isPropertyAccessExpression(expr) && expr.name.text === ENTRYPOINT && ts.isIdentifier(expr.expression)) {
|
|
23
|
+
return imports.includes(checker, expr.expression, PACKAGE_NAME);
|
|
17
24
|
}
|
|
18
25
|
return false;
|
|
19
26
|
}
|
|
@@ -24,15 +31,17 @@ export default {
|
|
|
24
31
|
if (!checker) {
|
|
25
32
|
return {};
|
|
26
33
|
}
|
|
27
|
-
let bindings = new
|
|
34
|
+
let bindings = scope.create(checker), candidates = new Set(), intents = {
|
|
28
35
|
imports: [],
|
|
29
36
|
prepend: [],
|
|
30
37
|
replacements: []
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
if (
|
|
38
|
+
}, sourceFile = ctx.sourceFile;
|
|
39
|
+
collect(checker, sourceFile, candidates);
|
|
40
|
+
if (candidates.size === 0) {
|
|
34
41
|
return {};
|
|
35
42
|
}
|
|
43
|
+
let isReactiveCall = (node) => candidates.has(node);
|
|
44
|
+
let { calls, replacements } = primitives(sourceFile, bindings, isReactiveCall);
|
|
36
45
|
intents.replacements.push(...replacements);
|
|
37
46
|
let objects = object(sourceFile, bindings, isReactiveCall);
|
|
38
47
|
intents.prepend.push(...objects.prepend);
|
package/build/compiler/object.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
2
|
import { code, uid } from '@esportsplus/typescript/compiler';
|
|
3
3
|
import { NAMESPACE, TYPES } from './constants.js';
|
|
4
|
+
import scope from './bindings.js';
|
|
4
5
|
function analyzeProperty(prop, sourceFile) {
|
|
5
6
|
if (!ts.isPropertyAssignment(prop)) {
|
|
6
7
|
return null;
|
|
@@ -143,10 +144,9 @@ function visit(ctx, node) {
|
|
|
143
144
|
if (ctx.isReactiveCall(node)) {
|
|
144
145
|
let arg = node.arguments[0];
|
|
145
146
|
if (arg && ts.isObjectLiteralExpression(arg)) {
|
|
146
|
-
let properties = [], props = arg.properties,
|
|
147
|
-
|
|
148
|
-
varname =
|
|
149
|
-
}
|
|
147
|
+
let properties = [], props = arg.properties, target = node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)
|
|
148
|
+
? node.parent.name
|
|
149
|
+
: null, varname = target?.text ?? null;
|
|
150
150
|
for (let i = 0, n = props.length; i < n; i++) {
|
|
151
151
|
let prop = props[i];
|
|
152
152
|
if (ts.isSpreadAssignment(prop)) {
|
|
@@ -159,8 +159,12 @@ function visit(ctx, node) {
|
|
|
159
159
|
return;
|
|
160
160
|
}
|
|
161
161
|
properties.push(analyzed);
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
}
|
|
163
|
+
if (target) {
|
|
164
|
+
for (let i = 0, n = properties.length; i < n; i++) {
|
|
165
|
+
if (properties[i].type === TYPES.Array) {
|
|
166
|
+
scope.declarePath(ctx.bindings, target, properties[i].key);
|
|
167
|
+
}
|
|
164
168
|
}
|
|
165
169
|
}
|
|
166
170
|
ctx.calls.push({
|