@esportsplus/reactivity 0.24.4 → 0.25.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/reactive/index.d.ts +9 -4
- package/build/transformer/index.d.ts +6 -4
- package/build/transformer/index.js +39 -106
- package/build/transformer/plugins/vite.js +4 -7
- package/build/transformer/transforms/array.d.ts +2 -2
- package/build/transformer/transforms/array.js +22 -20
- package/build/transformer/transforms/object.d.ts +3 -9
- package/build/transformer/transforms/object.js +99 -88
- package/build/transformer/transforms/primitives.d.ts +3 -3
- package/build/transformer/transforms/primitives.js +184 -131
- package/build/transformer/transforms/utilities.d.ts +8 -0
- package/build/transformer/transforms/utilities.js +27 -0
- package/build/types.d.ts +7 -6
- package/package.json +1 -1
- package/src/reactive/index.ts +24 -6
- package/src/transformer/index.ts +45 -187
- package/src/transformer/plugins/vite.ts +5 -13
- package/src/transformer/transforms/array.ts +31 -35
- package/src/transformer/transforms/object.ts +137 -292
- package/src/transformer/transforms/primitives.ts +236 -230
- package/src/transformer/transforms/utilities.ts +45 -0
- package/src/types.ts +9 -8
- package/test/vite.config.ts +1 -1
- package/build/transformer/factory.d.ts +0 -13
- package/build/transformer/factory.js +0 -36
- package/src/transformer/factory.ts +0 -141
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { uid } from '@esportsplus/typescript/transformer';
|
|
2
|
-
import type { BindingType, Bindings
|
|
3
|
-
import {
|
|
4
|
-
createCommaExpr,
|
|
5
|
-
createComputedCall,
|
|
6
|
-
createPostfixIncrementExpr,
|
|
7
|
-
createReadCall,
|
|
8
|
-
createSetCall,
|
|
9
|
-
createSignalCall
|
|
10
|
-
} from '../factory';
|
|
1
|
+
import { uid, type Range } from '@esportsplus/typescript/transformer';
|
|
2
|
+
import type { BindingType, Bindings } from '~/types';
|
|
3
|
+
import { addMissingImports, applyReplacements, Replacement } from './utilities';
|
|
11
4
|
import { ts } from '@esportsplus/typescript';
|
|
12
5
|
|
|
13
6
|
|
|
7
|
+
interface ArgContext {
|
|
8
|
+
argStart: number;
|
|
9
|
+
innerReplacements: Replacement[];
|
|
10
|
+
neededImports: Set<string>;
|
|
11
|
+
scopedBindings: ScopeBinding[];
|
|
12
|
+
sourceFile: ts.SourceFile;
|
|
13
|
+
}
|
|
14
|
+
|
|
14
15
|
interface ScopeBinding {
|
|
15
16
|
name: string;
|
|
16
17
|
scope: ts.Node;
|
|
@@ -19,12 +20,12 @@ interface ScopeBinding {
|
|
|
19
20
|
|
|
20
21
|
interface TransformContext {
|
|
21
22
|
bindings: Bindings;
|
|
22
|
-
|
|
23
|
-
factory: ts.NodeFactory;
|
|
23
|
+
computedArgRanges: Range[];
|
|
24
24
|
hasReactiveImport: boolean;
|
|
25
25
|
neededImports: Set<string>;
|
|
26
|
-
|
|
26
|
+
replacements: Replacement[];
|
|
27
27
|
scopedBindings: ScopeBinding[];
|
|
28
|
+
sourceFile: ts.SourceFile;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
|
|
@@ -57,14 +58,14 @@ function findEnclosingScope(node: ts.Node): ts.Node {
|
|
|
57
58
|
|
|
58
59
|
while (current) {
|
|
59
60
|
if (
|
|
60
|
-
ts.isArrowFunction(current) ||
|
|
61
61
|
ts.isBlock(current) ||
|
|
62
|
-
ts.
|
|
63
|
-
ts.isForOfStatement(current) ||
|
|
64
|
-
ts.isForStatement(current) ||
|
|
62
|
+
ts.isSourceFile(current) ||
|
|
65
63
|
ts.isFunctionDeclaration(current) ||
|
|
66
64
|
ts.isFunctionExpression(current) ||
|
|
67
|
-
ts.
|
|
65
|
+
ts.isArrowFunction(current) ||
|
|
66
|
+
ts.isForStatement(current) ||
|
|
67
|
+
ts.isForInStatement(current) ||
|
|
68
|
+
ts.isForOfStatement(current)
|
|
68
69
|
) {
|
|
69
70
|
return current;
|
|
70
71
|
}
|
|
@@ -75,82 +76,70 @@ function findEnclosingScope(node: ts.Node): ts.Node {
|
|
|
75
76
|
return node.getSourceFile();
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
function getCompoundOperator(kind: ts.SyntaxKind):
|
|
79
|
+
function getCompoundOperator(kind: ts.SyntaxKind): string {
|
|
79
80
|
if (kind === ts.SyntaxKind.PlusEqualsToken) {
|
|
80
|
-
return
|
|
81
|
+
return '+';
|
|
81
82
|
}
|
|
82
83
|
else if (kind === ts.SyntaxKind.MinusEqualsToken) {
|
|
83
|
-
return
|
|
84
|
+
return '-';
|
|
84
85
|
}
|
|
85
86
|
else if (kind === ts.SyntaxKind.AsteriskEqualsToken) {
|
|
86
|
-
return
|
|
87
|
+
return '*';
|
|
87
88
|
}
|
|
88
89
|
else if (kind === ts.SyntaxKind.SlashEqualsToken) {
|
|
89
|
-
return
|
|
90
|
+
return '/';
|
|
90
91
|
}
|
|
91
92
|
else if (kind === ts.SyntaxKind.PercentEqualsToken) {
|
|
92
|
-
return
|
|
93
|
+
return '%';
|
|
93
94
|
}
|
|
94
95
|
else if (kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) {
|
|
95
|
-
return
|
|
96
|
+
return '**';
|
|
96
97
|
}
|
|
97
98
|
else if (kind === ts.SyntaxKind.AmpersandEqualsToken) {
|
|
98
|
-
return
|
|
99
|
+
return '&';
|
|
99
100
|
}
|
|
100
101
|
else if (kind === ts.SyntaxKind.BarEqualsToken) {
|
|
101
|
-
return
|
|
102
|
+
return '|';
|
|
102
103
|
}
|
|
103
104
|
else if (kind === ts.SyntaxKind.CaretEqualsToken) {
|
|
104
|
-
return
|
|
105
|
+
return '^';
|
|
105
106
|
}
|
|
106
107
|
else if (kind === ts.SyntaxKind.LessThanLessThanEqualsToken) {
|
|
107
|
-
return
|
|
108
|
+
return '<<';
|
|
108
109
|
}
|
|
109
110
|
else if (kind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken) {
|
|
110
|
-
return
|
|
111
|
+
return '>>';
|
|
111
112
|
}
|
|
112
113
|
else if (kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
|
|
113
|
-
return
|
|
114
|
+
return '>>>';
|
|
114
115
|
}
|
|
115
116
|
else if (kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
|
|
116
|
-
return
|
|
117
|
+
return '&&';
|
|
117
118
|
}
|
|
118
119
|
else if (kind === ts.SyntaxKind.BarBarEqualsToken) {
|
|
119
|
-
return
|
|
120
|
+
return '||';
|
|
120
121
|
}
|
|
121
122
|
else if (kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
|
|
122
|
-
return
|
|
123
|
+
return '??';
|
|
123
124
|
}
|
|
124
125
|
else {
|
|
125
|
-
return
|
|
126
|
+
return '+';
|
|
126
127
|
}
|
|
127
128
|
}
|
|
128
129
|
|
|
129
|
-
function
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (kind >= ts.SyntaxKind.PlusEqualsToken && kind <= ts.SyntaxKind.CaretEqualsToken) {
|
|
135
|
-
return 'compound';
|
|
136
|
-
}
|
|
130
|
+
function isInComputedRange(ranges: Range[], start: number, end: number): boolean {
|
|
131
|
+
for (let i = 0, n = ranges.length; i < n; i++) {
|
|
132
|
+
let r = ranges[i];
|
|
137
133
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
kind === ts.SyntaxKind.QuestionQuestionEqualsToken
|
|
142
|
-
) {
|
|
143
|
-
return 'compound';
|
|
134
|
+
if (start >= r.start && end <= r.end) {
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
144
137
|
}
|
|
145
138
|
|
|
146
139
|
return false;
|
|
147
140
|
}
|
|
148
141
|
|
|
149
|
-
function isInDeclarationInit(node: ts.Node
|
|
150
|
-
if (!node || !node.parent) {
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
|
|
142
|
+
function isInDeclarationInit(node: ts.Node): boolean {
|
|
154
143
|
let parent = node.parent;
|
|
155
144
|
|
|
156
145
|
if (ts.isVariableDeclaration(parent) && parent.initializer === node) {
|
|
@@ -174,13 +163,16 @@ function isInScope(reference: ts.Node, binding: ScopeBinding): boolean {
|
|
|
174
163
|
return false;
|
|
175
164
|
}
|
|
176
165
|
|
|
177
|
-
function isReactiveReassignment(node: ts.
|
|
178
|
-
let
|
|
166
|
+
function isReactiveReassignment(node: ts.Node): boolean {
|
|
167
|
+
let parent = node.parent;
|
|
179
168
|
|
|
180
169
|
if (
|
|
181
|
-
ts.
|
|
182
|
-
ts.
|
|
183
|
-
right
|
|
170
|
+
ts.isBinaryExpression(parent) &&
|
|
171
|
+
parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
172
|
+
parent.right === node &&
|
|
173
|
+
ts.isCallExpression(node) &&
|
|
174
|
+
ts.isIdentifier((node as ts.CallExpression).expression) &&
|
|
175
|
+
((node as ts.CallExpression).expression as ts.Identifier).text === 'reactive'
|
|
184
176
|
) {
|
|
185
177
|
return true;
|
|
186
178
|
}
|
|
@@ -188,8 +180,41 @@ function isReactiveReassignment(node: ts.BinaryExpression): boolean {
|
|
|
188
180
|
return false;
|
|
189
181
|
}
|
|
190
182
|
|
|
191
|
-
function
|
|
192
|
-
|
|
183
|
+
function isWriteContext(node: ts.Identifier): 'simple' | 'compound' | 'increment' | false {
|
|
184
|
+
let parent = node.parent;
|
|
185
|
+
|
|
186
|
+
if (ts.isBinaryExpression(parent) && parent.left === node) {
|
|
187
|
+
let op = parent.operatorToken.kind;
|
|
188
|
+
|
|
189
|
+
if (op === ts.SyntaxKind.EqualsToken) {
|
|
190
|
+
return 'simple';
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (op >= ts.SyntaxKind.PlusEqualsToken && op <= ts.SyntaxKind.CaretEqualsToken) {
|
|
194
|
+
return 'compound';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (
|
|
198
|
+
op === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
|
|
199
|
+
op === ts.SyntaxKind.BarBarEqualsToken ||
|
|
200
|
+
op === ts.SyntaxKind.QuestionQuestionEqualsToken
|
|
201
|
+
) {
|
|
202
|
+
return 'compound';
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (ts.isPostfixUnaryExpression(parent) || ts.isPrefixUnaryExpression(parent)) {
|
|
207
|
+
let op = parent.operator;
|
|
208
|
+
|
|
209
|
+
if (op === ts.SyntaxKind.PlusPlusToken || op === ts.SyntaxKind.MinusMinusToken) {
|
|
210
|
+
return 'increment';
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function visit(ctx: TransformContext, node: ts.Node): void {
|
|
193
218
|
if (
|
|
194
219
|
ts.isImportDeclaration(node) &&
|
|
195
220
|
ts.isStringLiteral(node.moduleSpecifier) &&
|
|
@@ -207,7 +232,6 @@ function visit(ctx: TransformContext, node: ts.Node): ts.Node {
|
|
|
207
232
|
}
|
|
208
233
|
}
|
|
209
234
|
|
|
210
|
-
// Transform reactive() calls to signal() or computed()
|
|
211
235
|
if (
|
|
212
236
|
ctx.hasReactiveImport &&
|
|
213
237
|
ts.isCallExpression(node) &&
|
|
@@ -221,11 +245,10 @@ function visit(ctx: TransformContext, node: ts.Node): ts.Node {
|
|
|
221
245
|
if (classification) {
|
|
222
246
|
let varName: string | null = null;
|
|
223
247
|
|
|
224
|
-
if (
|
|
248
|
+
if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
225
249
|
varName = node.parent.name.text;
|
|
226
250
|
}
|
|
227
251
|
else if (
|
|
228
|
-
node.parent &&
|
|
229
252
|
ts.isBinaryExpression(node.parent) &&
|
|
230
253
|
node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
231
254
|
ts.isIdentifier(node.parent.left)
|
|
@@ -236,184 +259,155 @@ function visit(ctx: TransformContext, node: ts.Node): ts.Node {
|
|
|
236
259
|
if (varName) {
|
|
237
260
|
let scope = findEnclosingScope(node);
|
|
238
261
|
|
|
239
|
-
ctx.bindings.set(varName, classification);
|
|
240
262
|
ctx.scopedBindings.push({ name: varName, scope, type: classification });
|
|
263
|
+
ctx.bindings.set(varName, classification);
|
|
241
264
|
}
|
|
242
265
|
|
|
243
266
|
if (classification === 'computed') {
|
|
244
|
-
ctx.
|
|
267
|
+
ctx.computedArgRanges.push({
|
|
268
|
+
end: arg.end,
|
|
269
|
+
start: arg.getStart(ctx.sourceFile)
|
|
270
|
+
});
|
|
245
271
|
|
|
246
|
-
|
|
247
|
-
|
|
272
|
+
let argCtx: ArgContext = {
|
|
273
|
+
argStart: arg.getStart(ctx.sourceFile),
|
|
274
|
+
innerReplacements: [],
|
|
275
|
+
neededImports: ctx.neededImports,
|
|
276
|
+
scopedBindings: ctx.scopedBindings,
|
|
277
|
+
sourceFile: ctx.sourceFile
|
|
278
|
+
};
|
|
248
279
|
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
ctx.neededImports.add('signal');
|
|
280
|
+
visitArg(argCtx, arg);
|
|
253
281
|
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Handle binary expressions (assignments) with reactive left side
|
|
260
|
-
if (ts.isBinaryExpression(node) && ts.isIdentifier(node.left)) {
|
|
261
|
-
let assignType = isAssignmentOperator(node.operatorToken.kind);
|
|
262
|
-
|
|
263
|
-
if (assignType) {
|
|
264
|
-
let binding = findBinding(ctx.scopedBindings, node.left.text, node.left);
|
|
265
|
-
|
|
266
|
-
if (binding && binding.type !== 'computed' && !isReactiveReassignment(node)) {
|
|
267
|
-
ctx.neededImports.add('set');
|
|
282
|
+
let argText = applyReplacements(arg.getText(ctx.sourceFile), argCtx.innerReplacements);
|
|
268
283
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
284
|
+
ctx.replacements.push({
|
|
285
|
+
end: node.end,
|
|
286
|
+
newText: `computed(${argText})`,
|
|
287
|
+
start: node.pos
|
|
288
|
+
});
|
|
273
289
|
|
|
274
|
-
|
|
275
|
-
// x = value → ns.set(x, value)
|
|
276
|
-
return createSetCall(factory, ctx.ns.reactivity, signalIdent, transformedRight);
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
// x += value → ns.set(x, x.value + value)
|
|
280
|
-
let op = getCompoundOperator(node.operatorToken.kind),
|
|
281
|
-
valueAccess = factory.createPropertyAccessExpression(signalIdent, 'value');
|
|
282
|
-
|
|
283
|
-
return createSetCall(
|
|
284
|
-
factory,
|
|
285
|
-
ctx.ns.reactivity,
|
|
286
|
-
signalIdent,
|
|
287
|
-
factory.createBinaryExpression(valueAccess, op, transformedRight)
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Handle prefix unary expressions (++x, --x) with reactive operand
|
|
295
|
-
if (ts.isPrefixUnaryExpression(node) && ts.isIdentifier(node.operand)) {
|
|
296
|
-
let op = node.operator;
|
|
297
|
-
|
|
298
|
-
if (op === ts.SyntaxKind.PlusPlusToken || op === ts.SyntaxKind.MinusMinusToken) {
|
|
299
|
-
let binding = findBinding(ctx.scopedBindings, node.operand.text, node.operand);
|
|
300
|
-
|
|
301
|
-
if (binding && binding.type !== 'computed') {
|
|
302
|
-
ctx.neededImports.add('set');
|
|
303
|
-
|
|
304
|
-
let delta = op === ts.SyntaxKind.PlusPlusToken ? ts.SyntaxKind.PlusToken : ts.SyntaxKind.MinusToken,
|
|
305
|
-
factory = ctx.factory,
|
|
306
|
-
name = node.operand.text,
|
|
307
|
-
signalIdent = factory.createIdentifier(name),
|
|
308
|
-
valueAccess = factory.createPropertyAccessExpression(signalIdent, 'value');
|
|
309
|
-
|
|
310
|
-
if (node.parent && ts.isExpressionStatement(node.parent)) {
|
|
311
|
-
// ++x as statement → ns.set(x, x.value + 1)
|
|
312
|
-
return createSetCall(
|
|
313
|
-
factory,
|
|
314
|
-
ctx.ns.reactivity,
|
|
315
|
-
signalIdent,
|
|
316
|
-
factory.createBinaryExpression(valueAccess, delta, factory.createNumericLiteral(1))
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
else {
|
|
320
|
-
// ++x in expression → (ns.set(x, x.value + 1), x.value)
|
|
321
|
-
return createCommaExpr(
|
|
322
|
-
factory,
|
|
323
|
-
createSetCall(
|
|
324
|
-
factory,
|
|
325
|
-
ctx.ns.reactivity,
|
|
326
|
-
signalIdent,
|
|
327
|
-
factory.createBinaryExpression(valueAccess, delta, factory.createNumericLiteral(1))
|
|
328
|
-
),
|
|
329
|
-
factory.createPropertyAccessExpression(factory.createIdentifier(name), 'value')
|
|
330
|
-
);
|
|
331
|
-
}
|
|
290
|
+
ctx.neededImports.add('computed');
|
|
332
291
|
}
|
|
333
|
-
|
|
334
|
-
|
|
292
|
+
else {
|
|
293
|
+
let argText = arg.getText(ctx.sourceFile);
|
|
335
294
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
295
|
+
ctx.replacements.push({
|
|
296
|
+
end: node.end,
|
|
297
|
+
newText: `signal(${argText})`,
|
|
298
|
+
start: node.pos
|
|
299
|
+
});
|
|
339
300
|
|
|
340
|
-
|
|
341
|
-
let binding = findBinding(ctx.scopedBindings, node.operand.text, node.operand);
|
|
342
|
-
|
|
343
|
-
if (binding && binding.type !== 'computed') {
|
|
344
|
-
ctx.neededImports.add('set');
|
|
345
|
-
|
|
346
|
-
let delta = op === ts.SyntaxKind.PlusPlusToken ? ts.SyntaxKind.PlusToken : ts.SyntaxKind.MinusToken,
|
|
347
|
-
factory = ctx.factory,
|
|
348
|
-
name = node.operand.text,
|
|
349
|
-
signalIdent = factory.createIdentifier(name),
|
|
350
|
-
valueAccess = factory.createPropertyAccessExpression(signalIdent, 'value');
|
|
351
|
-
|
|
352
|
-
if (node.parent && ts.isExpressionStatement(node.parent)) {
|
|
353
|
-
// x++ as statement → ns.set(x, x.value + 1)
|
|
354
|
-
return createSetCall(
|
|
355
|
-
factory,
|
|
356
|
-
ctx.ns.reactivity,
|
|
357
|
-
signalIdent,
|
|
358
|
-
factory.createBinaryExpression(valueAccess, delta, factory.createNumericLiteral(1))
|
|
359
|
-
);
|
|
360
|
-
}
|
|
361
|
-
else {
|
|
362
|
-
// x++ in expression → ((tmp) => (ns.set(x, tmp + 1), tmp))(x.value)
|
|
363
|
-
return createPostfixIncrementExpr(factory, ctx.ns.reactivity, uid('tmp'), name, delta);
|
|
364
|
-
}
|
|
301
|
+
ctx.neededImports.add('signal');
|
|
365
302
|
}
|
|
366
303
|
}
|
|
367
304
|
}
|
|
368
305
|
|
|
369
|
-
|
|
370
|
-
if (ts.isIdentifier(node) && node.parent && !isInDeclarationInit(node.parent)) {
|
|
371
|
-
// Skip property names in property access expressions
|
|
306
|
+
if (ts.isIdentifier(node) && !isInDeclarationInit(node.parent)) {
|
|
372
307
|
if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
// Skip if this identifier is the left side of an assignment (handled above)
|
|
377
|
-
if (ts.isBinaryExpression(node.parent) && node.parent.left === node) {
|
|
378
|
-
return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
|
|
308
|
+
ts.forEachChild(node, n => visit(ctx, n));
|
|
309
|
+
return;
|
|
379
310
|
}
|
|
380
311
|
|
|
381
|
-
|
|
382
|
-
if (
|
|
383
|
-
(ts.isPrefixUnaryExpression(node.parent) || ts.isPostfixUnaryExpression(node.parent)) &&
|
|
384
|
-
node.parent.operand === node
|
|
385
|
-
) {
|
|
386
|
-
return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
|
|
387
|
-
}
|
|
312
|
+
let nodeStart = node.getStart(ctx.sourceFile);
|
|
388
313
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
return
|
|
314
|
+
if (isInComputedRange(ctx.computedArgRanges, nodeStart, node.end)) {
|
|
315
|
+
ts.forEachChild(node, n => visit(ctx, n));
|
|
316
|
+
return;
|
|
392
317
|
}
|
|
393
318
|
|
|
394
|
-
let binding = findBinding(ctx.scopedBindings, node.text, node)
|
|
319
|
+
let binding = findBinding(ctx.scopedBindings, node.text, node),
|
|
320
|
+
name = node.text;
|
|
395
321
|
|
|
396
322
|
if (binding) {
|
|
397
|
-
|
|
398
|
-
|
|
323
|
+
if (
|
|
324
|
+
!isReactiveReassignment(node.parent) &&
|
|
325
|
+
!(ts.isTypeOfExpression(node.parent) && node.parent.expression === node)
|
|
326
|
+
) {
|
|
327
|
+
let writeCtx = isWriteContext(node);
|
|
328
|
+
|
|
329
|
+
if (writeCtx) {
|
|
330
|
+
if (binding.type !== 'computed') {
|
|
331
|
+
ctx.neededImports.add('set');
|
|
332
|
+
|
|
333
|
+
let parent = node.parent;
|
|
334
|
+
|
|
335
|
+
if (writeCtx === 'simple' && ts.isBinaryExpression(parent)) {
|
|
336
|
+
let valueText = parent.right.getText(ctx.sourceFile);
|
|
337
|
+
|
|
338
|
+
ctx.replacements.push({
|
|
339
|
+
end: parent.end,
|
|
340
|
+
newText: `set(${name}, ${valueText})`,
|
|
341
|
+
start: parent.pos
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
else if (writeCtx === 'compound' && ts.isBinaryExpression(parent)) {
|
|
345
|
+
let op = getCompoundOperator(parent.operatorToken.kind),
|
|
346
|
+
valueText = parent.right.getText(ctx.sourceFile);
|
|
347
|
+
|
|
348
|
+
ctx.replacements.push({
|
|
349
|
+
end: parent.end,
|
|
350
|
+
newText: `set(${name}, ${name}.value ${op} ${valueText})`,
|
|
351
|
+
start: parent.pos
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
else if (writeCtx === 'increment') {
|
|
355
|
+
let isPrefix = ts.isPrefixUnaryExpression(parent),
|
|
356
|
+
op = (parent as ts.PrefixUnaryExpression | ts.PostfixUnaryExpression).operator,
|
|
357
|
+
delta = op === ts.SyntaxKind.PlusPlusToken ? '+ 1' : '- 1';
|
|
358
|
+
|
|
359
|
+
if (ts.isExpressionStatement(parent.parent)) {
|
|
360
|
+
ctx.replacements.push({
|
|
361
|
+
end: parent.end,
|
|
362
|
+
newText: `set(${name}, ${name}.value ${delta})`,
|
|
363
|
+
start: parent.pos
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
else if (isPrefix) {
|
|
367
|
+
ctx.replacements.push({
|
|
368
|
+
end: parent.end,
|
|
369
|
+
newText: `(set(${name}, ${name}.value ${delta}), ${name}.value)`,
|
|
370
|
+
start: parent.pos
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
let tmp = uid('tmp');
|
|
375
|
+
|
|
376
|
+
ctx.replacements.push({
|
|
377
|
+
end: parent.end,
|
|
378
|
+
newText: `((${tmp}) => (set(${name}, ${tmp} ${delta}), ${tmp}))(${name}.value)`,
|
|
379
|
+
start: parent.pos
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
ctx.neededImports.add('read');
|
|
399
387
|
|
|
400
|
-
|
|
388
|
+
ctx.replacements.push({
|
|
389
|
+
end: node.end,
|
|
390
|
+
newText: `read(${name})`,
|
|
391
|
+
start: node.pos
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
401
395
|
}
|
|
402
396
|
}
|
|
403
397
|
|
|
404
|
-
|
|
398
|
+
ts.forEachChild(node, n => visit(ctx, n));
|
|
405
399
|
}
|
|
406
400
|
|
|
407
|
-
function
|
|
408
|
-
|
|
409
|
-
if (ts.isIdentifier(node) && node.parent) {
|
|
401
|
+
function visitArg(ctx: ArgContext, node: ts.Node): void {
|
|
402
|
+
if (ts.isIdentifier(node)) {
|
|
410
403
|
if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
|
411
|
-
|
|
404
|
+
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
405
|
+
return;
|
|
412
406
|
}
|
|
413
407
|
|
|
414
|
-
// Skip function call expressions
|
|
415
408
|
if (ts.isCallExpression(node.parent) && node.parent.expression === node) {
|
|
416
|
-
|
|
409
|
+
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
410
|
+
return;
|
|
417
411
|
}
|
|
418
412
|
|
|
419
413
|
let binding = findBinding(ctx.scopedBindings, node.text, node);
|
|
@@ -421,35 +415,47 @@ function visitComputedArg(ctx: TransformContext, node: ts.Node): ts.Node {
|
|
|
421
415
|
if (binding) {
|
|
422
416
|
ctx.neededImports.add('read');
|
|
423
417
|
|
|
424
|
-
|
|
418
|
+
ctx.innerReplacements.push({
|
|
419
|
+
end: node.end - ctx.argStart,
|
|
420
|
+
newText: `read(${node.text})`,
|
|
421
|
+
start: node.getStart(ctx.sourceFile) - ctx.argStart
|
|
422
|
+
});
|
|
425
423
|
}
|
|
426
424
|
}
|
|
427
425
|
|
|
428
|
-
|
|
426
|
+
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
429
427
|
}
|
|
430
428
|
|
|
431
429
|
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
ns,
|
|
446
|
-
scopedBindings: []
|
|
447
|
-
};
|
|
448
|
-
|
|
449
|
-
return ts.visitNode(sourceFile, n => visit(ctx, n)) as ts.SourceFile;
|
|
430
|
+
const transformReactivePrimitives = (
|
|
431
|
+
sourceFile: ts.SourceFile,
|
|
432
|
+
bindings: Bindings
|
|
433
|
+
): string => {
|
|
434
|
+
let code = sourceFile.getFullText(),
|
|
435
|
+
ctx: TransformContext = {
|
|
436
|
+
bindings,
|
|
437
|
+
computedArgRanges: [],
|
|
438
|
+
hasReactiveImport: false,
|
|
439
|
+
neededImports: new Set<string>(),
|
|
440
|
+
replacements: [],
|
|
441
|
+
scopedBindings: [],
|
|
442
|
+
sourceFile
|
|
450
443
|
};
|
|
451
|
-
|
|
444
|
+
|
|
445
|
+
visit(ctx, sourceFile);
|
|
446
|
+
|
|
447
|
+
if (ctx.replacements.length === 0) {
|
|
448
|
+
return code;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
let result = applyReplacements(code, ctx.replacements);
|
|
452
|
+
|
|
453
|
+
if (ctx.neededImports.size > 0) {
|
|
454
|
+
result = addMissingImports(result, ctx.neededImports);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return result;
|
|
452
458
|
};
|
|
453
459
|
|
|
454
460
|
|
|
455
|
-
export {
|
|
461
|
+
export { transformReactivePrimitives };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { addImport, applyReplacements, type Replacement } from '@esportsplus/typescript/transformer';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
type ExtraImport = {
|
|
5
|
+
module: string;
|
|
6
|
+
specifier: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const addMissingImports = (code: string, needed: Set<string>, extraImports?: ExtraImport[]): string => {
|
|
11
|
+
let extraSpecifiers = new Set<string>(),
|
|
12
|
+
reactivitySpecifiers: string[] = [];
|
|
13
|
+
|
|
14
|
+
if (extraImports) {
|
|
15
|
+
for (let i = 0, n = extraImports.length; i < n; i++) {
|
|
16
|
+
extraSpecifiers.add(extraImports[i].specifier);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
for (let imp of needed) {
|
|
21
|
+
if (!extraSpecifiers.has(imp)) {
|
|
22
|
+
reactivitySpecifiers.push(imp);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (reactivitySpecifiers.length > 0) {
|
|
27
|
+
code = addImport(code, '@esportsplus/reactivity', reactivitySpecifiers);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (extraImports) {
|
|
31
|
+
for (let i = 0, n = extraImports.length; i < n; i++) {
|
|
32
|
+
let extra = extraImports[i];
|
|
33
|
+
|
|
34
|
+
if (needed.has(extra.specifier)) {
|
|
35
|
+
code = addImport(code, extra.module, [extra.specifier]);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return code;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
export { addMissingImports, applyReplacements };
|
|
45
|
+
export type { ExtraImport, Replacement };
|