@esportsplus/reactivity 0.29.21 → 0.30.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/build/compiler/array.js +85 -10
- package/build/reactive/array.d.ts +2 -1
- package/build/reactive/array.js +7 -1
- package/package.json +2 -2
- package/src/compiler/array.ts +104 -17
- package/src/reactive/array.ts +10 -1
package/build/compiler/array.js
CHANGED
|
@@ -24,6 +24,38 @@ function getElementTypeText(typeNode, sourceFile) {
|
|
|
24
24
|
}
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
27
|
+
function getOperator(kind) {
|
|
28
|
+
switch (kind) {
|
|
29
|
+
case ts.SyntaxKind.PlusEqualsToken: return '+';
|
|
30
|
+
case ts.SyntaxKind.MinusEqualsToken: return '-';
|
|
31
|
+
case ts.SyntaxKind.AsteriskEqualsToken: return '*';
|
|
32
|
+
case ts.SyntaxKind.SlashEqualsToken: return '/';
|
|
33
|
+
case ts.SyntaxKind.PercentEqualsToken: return '%';
|
|
34
|
+
case ts.SyntaxKind.AsteriskAsteriskEqualsToken: return '**';
|
|
35
|
+
case ts.SyntaxKind.AmpersandEqualsToken: return '&';
|
|
36
|
+
case ts.SyntaxKind.BarEqualsToken: return '|';
|
|
37
|
+
case ts.SyntaxKind.CaretEqualsToken: return '^';
|
|
38
|
+
default: return '+';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function isAssignmentOperator(kind) {
|
|
42
|
+
return kind === ts.SyntaxKind.EqualsToken ||
|
|
43
|
+
kind === ts.SyntaxKind.PlusEqualsToken ||
|
|
44
|
+
kind === ts.SyntaxKind.MinusEqualsToken ||
|
|
45
|
+
kind === ts.SyntaxKind.AsteriskEqualsToken ||
|
|
46
|
+
kind === ts.SyntaxKind.SlashEqualsToken ||
|
|
47
|
+
kind === ts.SyntaxKind.PercentEqualsToken ||
|
|
48
|
+
kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken ||
|
|
49
|
+
kind === ts.SyntaxKind.LessThanLessThanEqualsToken ||
|
|
50
|
+
kind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken ||
|
|
51
|
+
kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken ||
|
|
52
|
+
kind === ts.SyntaxKind.AmpersandEqualsToken ||
|
|
53
|
+
kind === ts.SyntaxKind.BarEqualsToken ||
|
|
54
|
+
kind === ts.SyntaxKind.CaretEqualsToken ||
|
|
55
|
+
kind === ts.SyntaxKind.BarBarEqualsToken ||
|
|
56
|
+
kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
|
|
57
|
+
kind === ts.SyntaxKind.QuestionQuestionEqualsToken;
|
|
58
|
+
}
|
|
27
59
|
function visit(ctx, node) {
|
|
28
60
|
if (isReactiveCall(ctx.checker, node) && node.arguments.length > 0) {
|
|
29
61
|
let arg = node.arguments[0], expression = ts.isAsExpression(arg) ? arg.expression : arg;
|
|
@@ -69,18 +101,45 @@ function visit(ctx, node) {
|
|
|
69
101
|
}
|
|
70
102
|
}
|
|
71
103
|
}
|
|
72
|
-
if (ts.isPropertyAccessExpression(node) &&
|
|
73
|
-
node.name.text === 'length' &&
|
|
74
|
-
(!node.parent ||
|
|
75
|
-
(!(ts.isBinaryExpression(node.parent) && node.parent.left === node) &&
|
|
76
|
-
!ts.isPostfixUnaryExpression(node.parent) &&
|
|
77
|
-
!ts.isPrefixUnaryExpression(node.parent)))) {
|
|
104
|
+
if (ts.isPropertyAccessExpression(node) && node.name.text === 'length') {
|
|
78
105
|
let name = ast.expression.name(node.expression);
|
|
79
106
|
if (name && ctx.bindings.get(name) === TYPES.Array) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
107
|
+
let expr = node.expression, parent = node.parent;
|
|
108
|
+
if (parent && ts.isBinaryExpression(parent) && parent.left === node && isAssignmentOperator(parent.operatorToken.kind)) {
|
|
109
|
+
let op = parent.operatorToken.kind;
|
|
110
|
+
if (op === ts.SyntaxKind.EqualsToken) {
|
|
111
|
+
ctx.replacements.push({
|
|
112
|
+
node: parent,
|
|
113
|
+
generate: (sf) => `${expr.getText(sf)}.$length = ${parent.right.getText(sf)}`
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
ctx.replacements.push({
|
|
118
|
+
node: parent,
|
|
119
|
+
generate: (sf) => `${expr.getText(sf)}.$length = ${expr.getText(sf)}.length ${getOperator(op)} ${parent.right.getText(sf)}`
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (parent && ts.isPostfixUnaryExpression(parent)) {
|
|
124
|
+
let op = parent.operator === ts.SyntaxKind.PlusPlusToken ? '+' : '-';
|
|
125
|
+
ctx.replacements.push({
|
|
126
|
+
node: parent,
|
|
127
|
+
generate: (sf) => `${expr.getText(sf)}.$length = ${expr.getText(sf)}.length ${op} 1`
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else if (parent && ts.isPrefixUnaryExpression(parent)) {
|
|
131
|
+
let op = parent.operator === ts.SyntaxKind.PlusPlusToken ? '+' : '-';
|
|
132
|
+
ctx.replacements.push({
|
|
133
|
+
node: parent,
|
|
134
|
+
generate: (sf) => `${expr.getText(sf)}.$length = ${expr.getText(sf)}.length ${op} 1`
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
ctx.replacements.push({
|
|
139
|
+
node,
|
|
140
|
+
generate: (sf) => `${expr.getText(sf)}.$length`
|
|
141
|
+
});
|
|
142
|
+
}
|
|
84
143
|
}
|
|
85
144
|
}
|
|
86
145
|
if (ts.isBinaryExpression(node) &&
|
|
@@ -97,6 +156,22 @@ function visit(ctx, node) {
|
|
|
97
156
|
});
|
|
98
157
|
}
|
|
99
158
|
}
|
|
159
|
+
if (ts.isBinaryExpression(node) &&
|
|
160
|
+
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
161
|
+
ts.isIdentifier(node.left)) {
|
|
162
|
+
let name = node.left.text, right = node.right;
|
|
163
|
+
while (ts.isAsExpression(right) || ts.isTypeAssertionExpression(right)) {
|
|
164
|
+
right = right.expression;
|
|
165
|
+
}
|
|
166
|
+
if (ctx.bindings.get(name) === TYPES.Array && ts.isArrayLiteralExpression(right)) {
|
|
167
|
+
ctx.replacements.push({
|
|
168
|
+
node,
|
|
169
|
+
generate: (sf) => right.elements.length > 0
|
|
170
|
+
? `${name}.splice(0, ${name}.length, ...${right.getText(sf)})`
|
|
171
|
+
: `${name}.splice(0, ${name}.length)`
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
100
175
|
ts.forEachChild(node, n => visit(ctx, n));
|
|
101
176
|
}
|
|
102
177
|
export default (sourceFile, bindings, checker) => {
|
|
@@ -38,7 +38,8 @@ declare class ReactiveArray<T> extends Array<T> {
|
|
|
38
38
|
private _length;
|
|
39
39
|
listeners: Listeners;
|
|
40
40
|
constructor(...items: T[]);
|
|
41
|
-
$length(): number;
|
|
41
|
+
get $length(): number;
|
|
42
|
+
set $length(value: number);
|
|
42
43
|
$set(i: number, value: T): void;
|
|
43
44
|
clear(): void;
|
|
44
45
|
concat(...items: ConcatArray<T>[]): ReactiveArray<T>;
|
package/build/reactive/array.js
CHANGED
|
@@ -14,9 +14,15 @@ class ReactiveArray extends Array {
|
|
|
14
14
|
super(...items);
|
|
15
15
|
this._length = signal(items.length);
|
|
16
16
|
}
|
|
17
|
-
$length() {
|
|
17
|
+
get $length() {
|
|
18
18
|
return read(this._length);
|
|
19
19
|
}
|
|
20
|
+
set $length(value) {
|
|
21
|
+
if (value > super.length) {
|
|
22
|
+
throw Error(`@esportsplus/reactivity: cannot set length to a value larger than the current length, use splice instead.`);
|
|
23
|
+
}
|
|
24
|
+
this.splice(value, super.length);
|
|
25
|
+
}
|
|
20
26
|
$set(i, value) {
|
|
21
27
|
let prev = this[i];
|
|
22
28
|
if (prev === value) {
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"@esportsplus/utilities": "^0.27.2"
|
|
5
5
|
},
|
|
6
6
|
"devDependencies": {
|
|
7
|
-
"@esportsplus/typescript": "^0.28.
|
|
7
|
+
"@esportsplus/typescript": "^0.28.4",
|
|
8
8
|
"@types/node": "^25.0.8",
|
|
9
9
|
"vite": "^7.3.1"
|
|
10
10
|
},
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"type": "module",
|
|
37
37
|
"types": "build/index.d.ts",
|
|
38
|
-
"version": "0.
|
|
38
|
+
"version": "0.30.0",
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "tsc",
|
|
41
41
|
"build:test": "pnpm build && vite build --config test/vite.config.ts",
|
package/src/compiler/array.ts
CHANGED
|
@@ -47,13 +47,47 @@ function getElementTypeText(typeNode: ts.TypeNode, sourceFile: ts.SourceFile): s
|
|
|
47
47
|
return null;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
function getOperator(kind: ts.SyntaxKind) {
|
|
51
|
+
switch (kind) {
|
|
52
|
+
case ts.SyntaxKind.PlusEqualsToken: return '+';
|
|
53
|
+
case ts.SyntaxKind.MinusEqualsToken: return '-';
|
|
54
|
+
case ts.SyntaxKind.AsteriskEqualsToken: return '*';
|
|
55
|
+
case ts.SyntaxKind.SlashEqualsToken: return '/';
|
|
56
|
+
case ts.SyntaxKind.PercentEqualsToken: return '%';
|
|
57
|
+
case ts.SyntaxKind.AsteriskAsteriskEqualsToken: return '**';
|
|
58
|
+
case ts.SyntaxKind.AmpersandEqualsToken: return '&';
|
|
59
|
+
case ts.SyntaxKind.BarEqualsToken: return '|';
|
|
60
|
+
case ts.SyntaxKind.CaretEqualsToken: return '^';
|
|
61
|
+
default: return '+';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isAssignmentOperator(kind: ts.SyntaxKind) {
|
|
66
|
+
return kind === ts.SyntaxKind.EqualsToken ||
|
|
67
|
+
kind === ts.SyntaxKind.PlusEqualsToken ||
|
|
68
|
+
kind === ts.SyntaxKind.MinusEqualsToken ||
|
|
69
|
+
kind === ts.SyntaxKind.AsteriskEqualsToken ||
|
|
70
|
+
kind === ts.SyntaxKind.SlashEqualsToken ||
|
|
71
|
+
kind === ts.SyntaxKind.PercentEqualsToken ||
|
|
72
|
+
kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken ||
|
|
73
|
+
kind === ts.SyntaxKind.LessThanLessThanEqualsToken ||
|
|
74
|
+
kind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken ||
|
|
75
|
+
kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken ||
|
|
76
|
+
kind === ts.SyntaxKind.AmpersandEqualsToken ||
|
|
77
|
+
kind === ts.SyntaxKind.BarEqualsToken ||
|
|
78
|
+
kind === ts.SyntaxKind.CaretEqualsToken ||
|
|
79
|
+
kind === ts.SyntaxKind.BarBarEqualsToken ||
|
|
80
|
+
kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
|
|
81
|
+
kind === ts.SyntaxKind.QuestionQuestionEqualsToken;
|
|
82
|
+
}
|
|
83
|
+
|
|
50
84
|
function visit(ctx: VisitContext, node: ts.Node): void {
|
|
51
85
|
if (isReactiveCall(ctx.checker, node) && node.arguments.length > 0) {
|
|
52
86
|
let arg = node.arguments[0],
|
|
53
87
|
expression = ts.isAsExpression(arg) ? arg.expression : arg;
|
|
54
88
|
|
|
55
89
|
if (ts.isArrayLiteralExpression(expression)) {
|
|
56
|
-
let elementType
|
|
90
|
+
let elementType = null;
|
|
57
91
|
|
|
58
92
|
if (ts.isAsExpression(arg) && arg.type) {
|
|
59
93
|
elementType = getElementTypeText(arg.type, ctx.sourceFile);
|
|
@@ -106,25 +140,55 @@ function visit(ctx: VisitContext, node: ts.Node): void {
|
|
|
106
140
|
}
|
|
107
141
|
}
|
|
108
142
|
|
|
109
|
-
if (
|
|
110
|
-
ts.isPropertyAccessExpression(node) &&
|
|
111
|
-
node.name.text === 'length' &&
|
|
112
|
-
(
|
|
113
|
-
!node.parent ||
|
|
114
|
-
(
|
|
115
|
-
!(ts.isBinaryExpression(node.parent) && node.parent.left === node) &&
|
|
116
|
-
!ts.isPostfixUnaryExpression(node.parent) &&
|
|
117
|
-
!ts.isPrefixUnaryExpression(node.parent)
|
|
118
|
-
)
|
|
119
|
-
)
|
|
120
|
-
) {
|
|
143
|
+
if (ts.isPropertyAccessExpression(node) && node.name.text === 'length') {
|
|
121
144
|
let name = ast.expression.name(node.expression);
|
|
122
145
|
|
|
123
146
|
if (name && ctx.bindings.get(name) === TYPES.Array) {
|
|
124
|
-
|
|
125
|
-
node
|
|
126
|
-
|
|
127
|
-
|
|
147
|
+
let expr = node.expression,
|
|
148
|
+
parent = node.parent;
|
|
149
|
+
|
|
150
|
+
// arr.length = value OR arr.length += value
|
|
151
|
+
if (parent && ts.isBinaryExpression(parent) && parent.left === node && isAssignmentOperator(parent.operatorToken.kind)) {
|
|
152
|
+
let op = parent.operatorToken.kind;
|
|
153
|
+
|
|
154
|
+
if (op === ts.SyntaxKind.EqualsToken) {
|
|
155
|
+
ctx.replacements.push({
|
|
156
|
+
node: parent,
|
|
157
|
+
generate: (sf) => `${expr.getText(sf)}.$length = ${parent.right.getText(sf)}`
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
ctx.replacements.push({
|
|
162
|
+
node: parent,
|
|
163
|
+
generate: (sf) => `${expr.getText(sf)}.$length = ${expr.getText(sf)}.length ${getOperator(op)} ${parent.right.getText(sf)}`
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// arr.length++ or arr.length--
|
|
168
|
+
else if (parent && ts.isPostfixUnaryExpression(parent)) {
|
|
169
|
+
let op = parent.operator === ts.SyntaxKind.PlusPlusToken ? '+' : '-';
|
|
170
|
+
|
|
171
|
+
ctx.replacements.push({
|
|
172
|
+
node: parent,
|
|
173
|
+
generate: (sf) => `${expr.getText(sf)}.$length = ${expr.getText(sf)}.length ${op} 1`
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
// ++arr.length or --arr.length
|
|
177
|
+
else if (parent && ts.isPrefixUnaryExpression(parent)) {
|
|
178
|
+
let op = parent.operator === ts.SyntaxKind.PlusPlusToken ? '+' : '-';
|
|
179
|
+
|
|
180
|
+
ctx.replacements.push({
|
|
181
|
+
node: parent,
|
|
182
|
+
generate: (sf) => `${expr.getText(sf)}.$length = ${expr.getText(sf)}.length ${op} 1`
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
// Read-only: arr.length → arr.$length
|
|
186
|
+
else {
|
|
187
|
+
ctx.replacements.push({
|
|
188
|
+
node,
|
|
189
|
+
generate: (sf) => `${expr.getText(sf)}.$length`
|
|
190
|
+
});
|
|
191
|
+
}
|
|
128
192
|
}
|
|
129
193
|
}
|
|
130
194
|
|
|
@@ -147,6 +211,29 @@ function visit(ctx: VisitContext, node: ts.Node): void {
|
|
|
147
211
|
}
|
|
148
212
|
}
|
|
149
213
|
|
|
214
|
+
if (
|
|
215
|
+
ts.isBinaryExpression(node) &&
|
|
216
|
+
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
217
|
+
ts.isIdentifier(node.left)
|
|
218
|
+
) {
|
|
219
|
+
let name = node.left.text,
|
|
220
|
+
right = node.right;
|
|
221
|
+
|
|
222
|
+
// Unwrap "as" expressions: arr = [] as Type[]
|
|
223
|
+
while (ts.isAsExpression(right) || ts.isTypeAssertionExpression(right)) {
|
|
224
|
+
right = right.expression;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (ctx.bindings.get(name) === TYPES.Array && ts.isArrayLiteralExpression(right)) {
|
|
228
|
+
ctx.replacements.push({
|
|
229
|
+
node,
|
|
230
|
+
generate: (sf) => right.elements.length > 0
|
|
231
|
+
? `${name}.splice(0, ${name}.length, ...${right.getText(sf)})`
|
|
232
|
+
: `${name}.splice(0, ${name}.length)`
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
150
237
|
ts.forEachChild(node, n => visit(ctx, n));
|
|
151
238
|
}
|
|
152
239
|
|
package/src/reactive/array.ts
CHANGED
|
@@ -64,10 +64,19 @@ class ReactiveArray<T> extends Array<T> {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
$length() {
|
|
67
|
+
get $length() {
|
|
68
68
|
return read(this._length);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
set $length(value: number) {
|
|
72
|
+
if (value > super.length) {
|
|
73
|
+
throw Error(`@esportsplus/reactivity: cannot set length to a value larger than the current length, use splice instead.`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.splice(value, super.length);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
71
80
|
$set(i: number, value: T) {
|
|
72
81
|
let prev = this[i];
|
|
73
82
|
|