@esportsplus/reactivity 0.24.5 → 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 -23
- package/build/transformer/transforms/object.d.ts +3 -9
- package/build/transformer/transforms/object.js +99 -89
- package/build/transformer/transforms/primitives.d.ts +3 -3
- package/build/transformer/transforms/primitives.js +193 -142
- 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 -40
- package/src/transformer/transforms/object.ts +137 -294
- package/src/transformer/transforms/primitives.ts +245 -242
- 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,29 +180,58 @@ function isReactiveReassignment(node: ts.BinaryExpression): boolean {
|
|
|
188
180
|
return false;
|
|
189
181
|
}
|
|
190
182
|
|
|
191
|
-
function
|
|
192
|
-
|
|
193
|
-
|
|
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
|
+
|
|
194
197
|
if (
|
|
195
|
-
ts.
|
|
196
|
-
|
|
198
|
+
op === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
|
|
199
|
+
op === ts.SyntaxKind.BarBarEqualsToken ||
|
|
200
|
+
op === ts.SyntaxKind.QuestionQuestionEqualsToken
|
|
197
201
|
) {
|
|
198
|
-
|
|
202
|
+
return 'compound';
|
|
203
|
+
}
|
|
204
|
+
}
|
|
199
205
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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 {
|
|
218
|
+
if (
|
|
219
|
+
ts.isImportDeclaration(node) &&
|
|
220
|
+
ts.isStringLiteral(node.moduleSpecifier) &&
|
|
221
|
+
node.moduleSpecifier.text.includes('@esportsplus/reactivity')
|
|
222
|
+
) {
|
|
223
|
+
let clause = node.importClause;
|
|
224
|
+
|
|
225
|
+
if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
|
|
226
|
+
for (let i = 0, n = clause.namedBindings.elements.length; i < n; i++) {
|
|
227
|
+
if (clause.namedBindings.elements[i].name.text === 'reactive') {
|
|
228
|
+
ctx.hasReactiveImport = true;
|
|
229
|
+
break;
|
|
206
230
|
}
|
|
207
231
|
}
|
|
208
232
|
}
|
|
209
|
-
|
|
210
|
-
return node;
|
|
211
233
|
}
|
|
212
234
|
|
|
213
|
-
// Transform reactive() calls to signal() or computed()
|
|
214
235
|
if (
|
|
215
236
|
ctx.hasReactiveImport &&
|
|
216
237
|
ts.isCallExpression(node) &&
|
|
@@ -224,11 +245,10 @@ function visit(ctx: TransformContext, node: ts.Node): ts.Node {
|
|
|
224
245
|
if (classification) {
|
|
225
246
|
let varName: string | null = null;
|
|
226
247
|
|
|
227
|
-
if (
|
|
248
|
+
if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
228
249
|
varName = node.parent.name.text;
|
|
229
250
|
}
|
|
230
251
|
else if (
|
|
231
|
-
node.parent &&
|
|
232
252
|
ts.isBinaryExpression(node.parent) &&
|
|
233
253
|
node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
234
254
|
ts.isIdentifier(node.parent.left)
|
|
@@ -239,184 +259,155 @@ function visit(ctx: TransformContext, node: ts.Node): ts.Node {
|
|
|
239
259
|
if (varName) {
|
|
240
260
|
let scope = findEnclosingScope(node);
|
|
241
261
|
|
|
242
|
-
ctx.bindings.set(varName, classification);
|
|
243
262
|
ctx.scopedBindings.push({ name: varName, scope, type: classification });
|
|
263
|
+
ctx.bindings.set(varName, classification);
|
|
244
264
|
}
|
|
245
265
|
|
|
246
266
|
if (classification === 'computed') {
|
|
247
|
-
ctx.
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return createComputedCall(ctx.factory, ctx.ns.reactivity, transformedArg as ts.Expression);
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
ctx.neededImports.add('signal');
|
|
256
|
-
|
|
257
|
-
return createSignalCall(ctx.factory, ctx.ns.reactivity, arg);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Handle binary expressions (assignments) with reactive left side
|
|
263
|
-
if (ts.isBinaryExpression(node) && ts.isIdentifier(node.left)) {
|
|
264
|
-
let assignType = isAssignmentOperator(node.operatorToken.kind);
|
|
265
|
-
|
|
266
|
-
if (assignType) {
|
|
267
|
-
let binding = findBinding(ctx.scopedBindings, node.left.text, node.left);
|
|
267
|
+
ctx.computedArgRanges.push({
|
|
268
|
+
end: arg.end,
|
|
269
|
+
start: arg.getStart(ctx.sourceFile)
|
|
270
|
+
});
|
|
268
271
|
|
|
269
|
-
|
|
270
|
-
|
|
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
|
+
};
|
|
271
279
|
|
|
272
|
-
|
|
273
|
-
name = node.left.text,
|
|
274
|
-
signalIdent = factory.createIdentifier(name),
|
|
275
|
-
transformedRight = ts.visitEachChild(node.right, n => visit(ctx, n), ctx.context) as ts.Expression;
|
|
280
|
+
visitArg(argCtx, arg);
|
|
276
281
|
|
|
277
|
-
|
|
278
|
-
// x = value → ns.set(x, value)
|
|
279
|
-
return createSetCall(factory, ctx.ns.reactivity, signalIdent, transformedRight);
|
|
280
|
-
}
|
|
281
|
-
else {
|
|
282
|
-
// x += value → ns.set(x, x.value + value)
|
|
283
|
-
let op = getCompoundOperator(node.operatorToken.kind),
|
|
284
|
-
valueAccess = factory.createPropertyAccessExpression(signalIdent, 'value');
|
|
285
|
-
|
|
286
|
-
return createSetCall(
|
|
287
|
-
factory,
|
|
288
|
-
ctx.ns.reactivity,
|
|
289
|
-
signalIdent,
|
|
290
|
-
factory.createBinaryExpression(valueAccess, op, transformedRight)
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
282
|
+
let argText = applyReplacements(arg.getText(ctx.sourceFile), argCtx.innerReplacements);
|
|
296
283
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
284
|
+
ctx.replacements.push({
|
|
285
|
+
end: node.end,
|
|
286
|
+
newText: `computed(${argText})`,
|
|
287
|
+
start: node.pos
|
|
288
|
+
});
|
|
300
289
|
|
|
301
|
-
|
|
302
|
-
let binding = findBinding(ctx.scopedBindings, node.operand.text, node.operand);
|
|
303
|
-
|
|
304
|
-
if (binding && binding.type !== 'computed') {
|
|
305
|
-
ctx.neededImports.add('set');
|
|
306
|
-
|
|
307
|
-
let delta = op === ts.SyntaxKind.PlusPlusToken ? ts.SyntaxKind.PlusToken : ts.SyntaxKind.MinusToken,
|
|
308
|
-
factory = ctx.factory,
|
|
309
|
-
name = node.operand.text,
|
|
310
|
-
signalIdent = factory.createIdentifier(name),
|
|
311
|
-
valueAccess = factory.createPropertyAccessExpression(signalIdent, 'value');
|
|
312
|
-
|
|
313
|
-
if (node.parent && ts.isExpressionStatement(node.parent)) {
|
|
314
|
-
// ++x as statement → ns.set(x, x.value + 1)
|
|
315
|
-
return createSetCall(
|
|
316
|
-
factory,
|
|
317
|
-
ctx.ns.reactivity,
|
|
318
|
-
signalIdent,
|
|
319
|
-
factory.createBinaryExpression(valueAccess, delta, factory.createNumericLiteral(1))
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
else {
|
|
323
|
-
// ++x in expression → (ns.set(x, x.value + 1), x.value)
|
|
324
|
-
return createCommaExpr(
|
|
325
|
-
factory,
|
|
326
|
-
createSetCall(
|
|
327
|
-
factory,
|
|
328
|
-
ctx.ns.reactivity,
|
|
329
|
-
signalIdent,
|
|
330
|
-
factory.createBinaryExpression(valueAccess, delta, factory.createNumericLiteral(1))
|
|
331
|
-
),
|
|
332
|
-
factory.createPropertyAccessExpression(factory.createIdentifier(name), 'value')
|
|
333
|
-
);
|
|
334
|
-
}
|
|
290
|
+
ctx.neededImports.add('computed');
|
|
335
291
|
}
|
|
336
|
-
|
|
337
|
-
|
|
292
|
+
else {
|
|
293
|
+
let argText = arg.getText(ctx.sourceFile);
|
|
338
294
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
295
|
+
ctx.replacements.push({
|
|
296
|
+
end: node.end,
|
|
297
|
+
newText: `signal(${argText})`,
|
|
298
|
+
start: node.pos
|
|
299
|
+
});
|
|
342
300
|
|
|
343
|
-
|
|
344
|
-
let binding = findBinding(ctx.scopedBindings, node.operand.text, node.operand);
|
|
345
|
-
|
|
346
|
-
if (binding && binding.type !== 'computed') {
|
|
347
|
-
ctx.neededImports.add('set');
|
|
348
|
-
|
|
349
|
-
let delta = op === ts.SyntaxKind.PlusPlusToken ? ts.SyntaxKind.PlusToken : ts.SyntaxKind.MinusToken,
|
|
350
|
-
factory = ctx.factory,
|
|
351
|
-
name = node.operand.text,
|
|
352
|
-
signalIdent = factory.createIdentifier(name),
|
|
353
|
-
valueAccess = factory.createPropertyAccessExpression(signalIdent, 'value');
|
|
354
|
-
|
|
355
|
-
if (node.parent && ts.isExpressionStatement(node.parent)) {
|
|
356
|
-
// x++ as statement → ns.set(x, x.value + 1)
|
|
357
|
-
return createSetCall(
|
|
358
|
-
factory,
|
|
359
|
-
ctx.ns.reactivity,
|
|
360
|
-
signalIdent,
|
|
361
|
-
factory.createBinaryExpression(valueAccess, delta, factory.createNumericLiteral(1))
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
// x++ in expression → ((tmp) => (ns.set(x, tmp + 1), tmp))(x.value)
|
|
366
|
-
return createPostfixIncrementExpr(factory, ctx.ns.reactivity, uid('tmp'), name, delta);
|
|
367
|
-
}
|
|
301
|
+
ctx.neededImports.add('signal');
|
|
368
302
|
}
|
|
369
303
|
}
|
|
370
304
|
}
|
|
371
305
|
|
|
372
|
-
|
|
373
|
-
if (ts.isIdentifier(node) && node.parent && !isInDeclarationInit(node.parent)) {
|
|
374
|
-
// Skip property names in property access expressions
|
|
306
|
+
if (ts.isIdentifier(node) && !isInDeclarationInit(node.parent)) {
|
|
375
307
|
if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
// Skip if this identifier is the left side of an assignment (handled above)
|
|
380
|
-
if (ts.isBinaryExpression(node.parent) && node.parent.left === node) {
|
|
381
|
-
return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
|
|
308
|
+
ts.forEachChild(node, n => visit(ctx, n));
|
|
309
|
+
return;
|
|
382
310
|
}
|
|
383
311
|
|
|
384
|
-
|
|
385
|
-
if (
|
|
386
|
-
(ts.isPrefixUnaryExpression(node.parent) || ts.isPostfixUnaryExpression(node.parent)) &&
|
|
387
|
-
node.parent.operand === node
|
|
388
|
-
) {
|
|
389
|
-
return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
|
|
390
|
-
}
|
|
312
|
+
let nodeStart = node.getStart(ctx.sourceFile);
|
|
391
313
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
return
|
|
314
|
+
if (isInComputedRange(ctx.computedArgRanges, nodeStart, node.end)) {
|
|
315
|
+
ts.forEachChild(node, n => visit(ctx, n));
|
|
316
|
+
return;
|
|
395
317
|
}
|
|
396
318
|
|
|
397
|
-
let binding = findBinding(ctx.scopedBindings, node.text, node)
|
|
319
|
+
let binding = findBinding(ctx.scopedBindings, node.text, node),
|
|
320
|
+
name = node.text;
|
|
398
321
|
|
|
399
322
|
if (binding) {
|
|
400
|
-
|
|
401
|
-
|
|
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');
|
|
402
387
|
|
|
403
|
-
|
|
388
|
+
ctx.replacements.push({
|
|
389
|
+
end: node.end,
|
|
390
|
+
newText: `read(${name})`,
|
|
391
|
+
start: node.pos
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
404
395
|
}
|
|
405
396
|
}
|
|
406
397
|
|
|
407
|
-
|
|
398
|
+
ts.forEachChild(node, n => visit(ctx, n));
|
|
408
399
|
}
|
|
409
400
|
|
|
410
|
-
function
|
|
411
|
-
|
|
412
|
-
if (ts.isIdentifier(node) && node.parent) {
|
|
401
|
+
function visitArg(ctx: ArgContext, node: ts.Node): void {
|
|
402
|
+
if (ts.isIdentifier(node)) {
|
|
413
403
|
if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
|
414
|
-
|
|
404
|
+
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
405
|
+
return;
|
|
415
406
|
}
|
|
416
407
|
|
|
417
|
-
// Skip function call expressions
|
|
418
408
|
if (ts.isCallExpression(node.parent) && node.parent.expression === node) {
|
|
419
|
-
|
|
409
|
+
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
410
|
+
return;
|
|
420
411
|
}
|
|
421
412
|
|
|
422
413
|
let binding = findBinding(ctx.scopedBindings, node.text, node);
|
|
@@ -424,35 +415,47 @@ function visitComputedArg(ctx: TransformContext, node: ts.Node): ts.Node {
|
|
|
424
415
|
if (binding) {
|
|
425
416
|
ctx.neededImports.add('read');
|
|
426
417
|
|
|
427
|
-
|
|
418
|
+
ctx.innerReplacements.push({
|
|
419
|
+
end: node.end - ctx.argStart,
|
|
420
|
+
newText: `read(${node.text})`,
|
|
421
|
+
start: node.getStart(ctx.sourceFile) - ctx.argStart
|
|
422
|
+
});
|
|
428
423
|
}
|
|
429
424
|
}
|
|
430
425
|
|
|
431
|
-
|
|
426
|
+
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
432
427
|
}
|
|
433
428
|
|
|
434
429
|
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
ns,
|
|
449
|
-
scopedBindings: []
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
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
|
|
453
443
|
};
|
|
454
|
-
|
|
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;
|
|
455
458
|
};
|
|
456
459
|
|
|
457
460
|
|
|
458
|
-
export {
|
|
461
|
+
export { transformReactivePrimitives };
|