@esportsplus/reactivity 0.24.1 → 0.24.3

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.
@@ -1,5 +1,5 @@
1
1
  import { uid } from '@esportsplus/typescript/transformer';
2
- import { addMissingImports, applyReplacements } from './utilities.js';
2
+ import { createCommaExpr, createComputedCall, createPostfixIncrementExpr, createReadCall, createSetCall, createSignalCall } from '../factory.js';
3
3
  import { ts } from '@esportsplus/typescript';
4
4
  function classifyReactiveArg(arg) {
5
5
  if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
@@ -22,14 +22,14 @@ function findBinding(bindings, name, node) {
22
22
  function findEnclosingScope(node) {
23
23
  let current = node.parent;
24
24
  while (current) {
25
- if (ts.isBlock(current) ||
26
- ts.isSourceFile(current) ||
25
+ if (ts.isArrowFunction(current) ||
26
+ ts.isBlock(current) ||
27
+ ts.isForInStatement(current) ||
28
+ ts.isForOfStatement(current) ||
29
+ ts.isForStatement(current) ||
27
30
  ts.isFunctionDeclaration(current) ||
28
31
  ts.isFunctionExpression(current) ||
29
- ts.isArrowFunction(current) ||
30
- ts.isForStatement(current) ||
31
- ts.isForInStatement(current) ||
32
- ts.isForOfStatement(current)) {
32
+ ts.isSourceFile(current)) {
33
33
  return current;
34
34
  }
35
35
  current = current.parent;
@@ -38,64 +38,72 @@ function findEnclosingScope(node) {
38
38
  }
39
39
  function getCompoundOperator(kind) {
40
40
  if (kind === ts.SyntaxKind.PlusEqualsToken) {
41
- return '+';
41
+ return ts.SyntaxKind.PlusToken;
42
42
  }
43
43
  else if (kind === ts.SyntaxKind.MinusEqualsToken) {
44
- return '-';
44
+ return ts.SyntaxKind.MinusToken;
45
45
  }
46
46
  else if (kind === ts.SyntaxKind.AsteriskEqualsToken) {
47
- return '*';
47
+ return ts.SyntaxKind.AsteriskToken;
48
48
  }
49
49
  else if (kind === ts.SyntaxKind.SlashEqualsToken) {
50
- return '/';
50
+ return ts.SyntaxKind.SlashToken;
51
51
  }
52
52
  else if (kind === ts.SyntaxKind.PercentEqualsToken) {
53
- return '%';
53
+ return ts.SyntaxKind.PercentToken;
54
54
  }
55
55
  else if (kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) {
56
- return '**';
56
+ return ts.SyntaxKind.AsteriskAsteriskToken;
57
57
  }
58
58
  else if (kind === ts.SyntaxKind.AmpersandEqualsToken) {
59
- return '&';
59
+ return ts.SyntaxKind.AmpersandToken;
60
60
  }
61
61
  else if (kind === ts.SyntaxKind.BarEqualsToken) {
62
- return '|';
62
+ return ts.SyntaxKind.BarToken;
63
63
  }
64
64
  else if (kind === ts.SyntaxKind.CaretEqualsToken) {
65
- return '^';
65
+ return ts.SyntaxKind.CaretToken;
66
66
  }
67
67
  else if (kind === ts.SyntaxKind.LessThanLessThanEqualsToken) {
68
- return '<<';
68
+ return ts.SyntaxKind.LessThanLessThanToken;
69
69
  }
70
70
  else if (kind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken) {
71
- return '>>';
71
+ return ts.SyntaxKind.GreaterThanGreaterThanToken;
72
72
  }
73
73
  else if (kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
74
- return '>>>';
74
+ return ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken;
75
75
  }
76
76
  else if (kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
77
- return '&&';
77
+ return ts.SyntaxKind.AmpersandAmpersandToken;
78
78
  }
79
79
  else if (kind === ts.SyntaxKind.BarBarEqualsToken) {
80
- return '||';
80
+ return ts.SyntaxKind.BarBarToken;
81
81
  }
82
82
  else if (kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
83
- return '??';
83
+ return ts.SyntaxKind.QuestionQuestionToken;
84
84
  }
85
85
  else {
86
- return '+';
86
+ return ts.SyntaxKind.PlusToken;
87
87
  }
88
88
  }
89
- function isInComputedRange(ranges, start, end) {
90
- for (let i = 0, n = ranges.length; i < n; i++) {
91
- let r = ranges[i];
92
- if (start >= r.start && end <= r.end) {
93
- return true;
94
- }
89
+ function isAssignmentOperator(kind) {
90
+ if (kind === ts.SyntaxKind.EqualsToken) {
91
+ return 'simple';
92
+ }
93
+ if (kind >= ts.SyntaxKind.PlusEqualsToken && kind <= ts.SyntaxKind.CaretEqualsToken) {
94
+ return 'compound';
95
+ }
96
+ if (kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
97
+ kind === ts.SyntaxKind.BarBarEqualsToken ||
98
+ kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
99
+ return 'compound';
95
100
  }
96
101
  return false;
97
102
  }
98
103
  function isInDeclarationInit(node) {
104
+ if (!node || !node.parent) {
105
+ return false;
106
+ }
99
107
  let parent = node.parent;
100
108
  if (ts.isVariableDeclaration(parent) && parent.initializer === node) {
101
109
  return true;
@@ -113,41 +121,14 @@ function isInScope(reference, binding) {
113
121
  return false;
114
122
  }
115
123
  function isReactiveReassignment(node) {
116
- let parent = node.parent;
117
- if (ts.isBinaryExpression(parent) &&
118
- parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
119
- parent.right === node &&
120
- ts.isCallExpression(node) &&
121
- ts.isIdentifier(node.expression) &&
122
- node.expression.text === 'reactive') {
124
+ let right = node.right;
125
+ if (ts.isCallExpression(right) &&
126
+ ts.isIdentifier(right.expression) &&
127
+ right.expression.text === 'reactive') {
123
128
  return true;
124
129
  }
125
130
  return false;
126
131
  }
127
- function isWriteContext(node) {
128
- let parent = node.parent;
129
- if (ts.isBinaryExpression(parent) && parent.left === node) {
130
- let op = parent.operatorToken.kind;
131
- if (op === ts.SyntaxKind.EqualsToken) {
132
- return 'simple';
133
- }
134
- if (op >= ts.SyntaxKind.PlusEqualsToken && op <= ts.SyntaxKind.CaretEqualsToken) {
135
- return 'compound';
136
- }
137
- if (op === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
138
- op === ts.SyntaxKind.BarBarEqualsToken ||
139
- op === ts.SyntaxKind.QuestionQuestionEqualsToken) {
140
- return 'compound';
141
- }
142
- }
143
- if (ts.isPostfixUnaryExpression(parent) || ts.isPrefixUnaryExpression(parent)) {
144
- let op = parent.operator;
145
- if (op === ts.SyntaxKind.PlusPlusToken || op === ts.SyntaxKind.MinusMinusToken) {
146
- return 'increment';
147
- }
148
- }
149
- return false;
150
- }
151
132
  function visit(ctx, node) {
152
133
  if (ts.isImportDeclaration(node) &&
153
134
  ts.isStringLiteral(node.moduleSpecifier) &&
@@ -170,166 +151,132 @@ function visit(ctx, node) {
170
151
  let arg = node.arguments[0], classification = classifyReactiveArg(arg);
171
152
  if (classification) {
172
153
  let varName = null;
173
- if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
154
+ if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
174
155
  varName = node.parent.name.text;
175
156
  }
176
- else if (ts.isBinaryExpression(node.parent) &&
157
+ else if (node.parent &&
158
+ ts.isBinaryExpression(node.parent) &&
177
159
  node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
178
160
  ts.isIdentifier(node.parent.left)) {
179
161
  varName = node.parent.left.text;
180
162
  }
181
163
  if (varName) {
182
164
  let scope = findEnclosingScope(node);
183
- ctx.scopedBindings.push({ name: varName, scope, type: classification });
184
165
  ctx.bindings.set(varName, classification);
166
+ ctx.scopedBindings.push({ name: varName, scope, type: classification });
185
167
  }
186
168
  if (classification === 'computed') {
187
- ctx.computedArgRanges.push({
188
- end: arg.end,
189
- start: arg.getStart(ctx.sourceFile)
190
- });
191
- let argCtx = {
192
- argStart: arg.getStart(ctx.sourceFile),
193
- innerReplacements: [],
194
- neededImports: ctx.neededImports,
195
- scopedBindings: ctx.scopedBindings,
196
- sourceFile: ctx.sourceFile
197
- };
198
- visitArg(argCtx, arg);
199
- let argText = applyReplacements(arg.getText(ctx.sourceFile), argCtx.innerReplacements);
200
- ctx.replacements.push({
201
- end: node.end,
202
- newText: `computed(${argText})`,
203
- start: node.pos
204
- });
205
169
  ctx.neededImports.add('computed');
170
+ let transformedArg = ts.visitEachChild(arg, n => visitComputedArg(ctx, n), ctx.context);
171
+ return createComputedCall(ctx.factory, ctx.ns.reactivity, transformedArg);
206
172
  }
207
173
  else {
208
- let argText = arg.getText(ctx.sourceFile);
209
- ctx.replacements.push({
210
- end: node.end,
211
- newText: `signal(${argText})`,
212
- start: node.pos
213
- });
214
174
  ctx.neededImports.add('signal');
175
+ return createSignalCall(ctx.factory, ctx.ns.reactivity, arg);
215
176
  }
216
177
  }
217
178
  }
218
- if (ts.isIdentifier(node) && !isInDeclarationInit(node.parent)) {
219
- if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
220
- ts.forEachChild(node, n => visit(ctx, n));
221
- return;
179
+ if (ts.isBinaryExpression(node) && ts.isIdentifier(node.left)) {
180
+ let assignType = isAssignmentOperator(node.operatorToken.kind);
181
+ if (assignType) {
182
+ let binding = findBinding(ctx.scopedBindings, node.left.text, node.left);
183
+ if (binding && binding.type !== 'computed' && !isReactiveReassignment(node)) {
184
+ ctx.neededImports.add('set');
185
+ let factory = ctx.factory, name = node.left.text, signalIdent = factory.createIdentifier(name), transformedRight = ts.visitEachChild(node.right, n => visit(ctx, n), ctx.context);
186
+ if (assignType === 'simple') {
187
+ return createSetCall(factory, ctx.ns.reactivity, signalIdent, transformedRight);
188
+ }
189
+ else {
190
+ let op = getCompoundOperator(node.operatorToken.kind), valueAccess = factory.createPropertyAccessExpression(signalIdent, 'value');
191
+ return createSetCall(factory, ctx.ns.reactivity, signalIdent, factory.createBinaryExpression(valueAccess, op, transformedRight));
192
+ }
193
+ }
222
194
  }
223
- let nodeStart = node.getStart(ctx.sourceFile);
224
- if (isInComputedRange(ctx.computedArgRanges, nodeStart, node.end)) {
225
- ts.forEachChild(node, n => visit(ctx, n));
226
- return;
195
+ }
196
+ if (ts.isPrefixUnaryExpression(node) && ts.isIdentifier(node.operand)) {
197
+ let op = node.operator;
198
+ if (op === ts.SyntaxKind.PlusPlusToken || op === ts.SyntaxKind.MinusMinusToken) {
199
+ let binding = findBinding(ctx.scopedBindings, node.operand.text, node.operand);
200
+ if (binding && binding.type !== 'computed') {
201
+ ctx.neededImports.add('set');
202
+ let delta = op === ts.SyntaxKind.PlusPlusToken ? ts.SyntaxKind.PlusToken : ts.SyntaxKind.MinusToken, factory = ctx.factory, name = node.operand.text, signalIdent = factory.createIdentifier(name), valueAccess = factory.createPropertyAccessExpression(signalIdent, 'value');
203
+ if (node.parent && ts.isExpressionStatement(node.parent)) {
204
+ return createSetCall(factory, ctx.ns.reactivity, signalIdent, factory.createBinaryExpression(valueAccess, delta, factory.createNumericLiteral(1)));
205
+ }
206
+ else {
207
+ return createCommaExpr(factory, createSetCall(factory, ctx.ns.reactivity, signalIdent, factory.createBinaryExpression(valueAccess, delta, factory.createNumericLiteral(1))), factory.createPropertyAccessExpression(factory.createIdentifier(name), 'value'));
208
+ }
209
+ }
227
210
  }
228
- let binding = findBinding(ctx.scopedBindings, node.text, node), name = node.text;
229
- if (binding) {
230
- if (!isReactiveReassignment(node.parent) &&
231
- !(ts.isTypeOfExpression(node.parent) && node.parent.expression === node)) {
232
- let writeCtx = isWriteContext(node);
233
- if (writeCtx) {
234
- if (binding.type !== 'computed') {
235
- ctx.neededImports.add('set');
236
- let parent = node.parent;
237
- if (writeCtx === 'simple' && ts.isBinaryExpression(parent)) {
238
- let valueText = parent.right.getText(ctx.sourceFile);
239
- ctx.replacements.push({
240
- end: parent.end,
241
- newText: `set(${name}, ${valueText})`,
242
- start: parent.pos
243
- });
244
- }
245
- else if (writeCtx === 'compound' && ts.isBinaryExpression(parent)) {
246
- let op = getCompoundOperator(parent.operatorToken.kind), valueText = parent.right.getText(ctx.sourceFile);
247
- ctx.replacements.push({
248
- end: parent.end,
249
- newText: `set(${name}, ${name}.value ${op} ${valueText})`,
250
- start: parent.pos
251
- });
252
- }
253
- else if (writeCtx === 'increment') {
254
- let isPrefix = ts.isPrefixUnaryExpression(parent), op = parent.operator, delta = op === ts.SyntaxKind.PlusPlusToken ? '+ 1' : '- 1';
255
- if (ts.isExpressionStatement(parent.parent)) {
256
- ctx.replacements.push({
257
- end: parent.end,
258
- newText: `set(${name}, ${name}.value ${delta})`,
259
- start: parent.pos
260
- });
261
- }
262
- else if (isPrefix) {
263
- ctx.replacements.push({
264
- end: parent.end,
265
- newText: `(set(${name}, ${name}.value ${delta}), ${name}.value)`,
266
- start: parent.pos
267
- });
268
- }
269
- else {
270
- let tmp = uid('tmp');
271
- ctx.replacements.push({
272
- end: parent.end,
273
- newText: `((${tmp}) => (set(${name}, ${tmp} ${delta}), ${tmp}))(${name}.value)`,
274
- start: parent.pos
275
- });
276
- }
277
- }
278
- }
211
+ }
212
+ if (ts.isPostfixUnaryExpression(node) && ts.isIdentifier(node.operand)) {
213
+ let op = node.operator;
214
+ if (op === ts.SyntaxKind.PlusPlusToken || op === ts.SyntaxKind.MinusMinusToken) {
215
+ let binding = findBinding(ctx.scopedBindings, node.operand.text, node.operand);
216
+ if (binding && binding.type !== 'computed') {
217
+ ctx.neededImports.add('set');
218
+ let delta = op === ts.SyntaxKind.PlusPlusToken ? ts.SyntaxKind.PlusToken : ts.SyntaxKind.MinusToken, factory = ctx.factory, name = node.operand.text, signalIdent = factory.createIdentifier(name), valueAccess = factory.createPropertyAccessExpression(signalIdent, 'value');
219
+ if (node.parent && ts.isExpressionStatement(node.parent)) {
220
+ return createSetCall(factory, ctx.ns.reactivity, signalIdent, factory.createBinaryExpression(valueAccess, delta, factory.createNumericLiteral(1)));
279
221
  }
280
222
  else {
281
- ctx.neededImports.add('read');
282
- ctx.replacements.push({
283
- end: node.end,
284
- newText: `read(${name})`,
285
- start: node.pos
286
- });
223
+ return createPostfixIncrementExpr(factory, ctx.ns.reactivity, uid('tmp'), name, delta);
287
224
  }
288
225
  }
289
226
  }
290
227
  }
291
- ts.forEachChild(node, n => visit(ctx, n));
228
+ if (ts.isIdentifier(node) && node.parent && !isInDeclarationInit(node.parent)) {
229
+ if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
230
+ return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
231
+ }
232
+ if (ts.isBinaryExpression(node.parent) && node.parent.left === node) {
233
+ return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
234
+ }
235
+ if ((ts.isPrefixUnaryExpression(node.parent) || ts.isPostfixUnaryExpression(node.parent)) &&
236
+ node.parent.operand === node) {
237
+ return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
238
+ }
239
+ if (ts.isTypeOfExpression(node.parent) && node.parent.expression === node) {
240
+ return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
241
+ }
242
+ let binding = findBinding(ctx.scopedBindings, node.text, node);
243
+ if (binding) {
244
+ ctx.neededImports.add('read');
245
+ return createReadCall(ctx.factory, ctx.ns.reactivity, ctx.factory.createIdentifier(node.text));
246
+ }
247
+ }
248
+ return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
292
249
  }
293
- function visitArg(ctx, node) {
294
- if (ts.isIdentifier(node)) {
250
+ function visitComputedArg(ctx, node) {
251
+ if (ts.isIdentifier(node) && node.parent) {
295
252
  if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
296
- ts.forEachChild(node, n => visitArg(ctx, n));
297
- return;
253
+ return ts.visitEachChild(node, n => visitComputedArg(ctx, n), ctx.context);
298
254
  }
299
255
  if (ts.isCallExpression(node.parent) && node.parent.expression === node) {
300
- ts.forEachChild(node, n => visitArg(ctx, n));
301
- return;
256
+ return ts.visitEachChild(node, n => visitComputedArg(ctx, n), ctx.context);
302
257
  }
303
258
  let binding = findBinding(ctx.scopedBindings, node.text, node);
304
259
  if (binding) {
305
260
  ctx.neededImports.add('read');
306
- ctx.innerReplacements.push({
307
- end: node.end - ctx.argStart,
308
- newText: `read(${node.text})`,
309
- start: node.getStart(ctx.sourceFile) - ctx.argStart
310
- });
261
+ return createReadCall(ctx.factory, ctx.ns.reactivity, ctx.factory.createIdentifier(node.text));
311
262
  }
312
263
  }
313
- ts.forEachChild(node, n => visitArg(ctx, n));
264
+ return ts.visitEachChild(node, n => visitComputedArg(ctx, n), ctx.context);
314
265
  }
315
- const transformReactivePrimitives = (sourceFile, bindings) => {
316
- let code = sourceFile.getFullText(), ctx = {
317
- bindings,
318
- computedArgRanges: [],
319
- hasReactiveImport: false,
320
- neededImports: new Set(),
321
- replacements: [],
322
- scopedBindings: [],
323
- sourceFile
266
+ const createPrimitivesTransformer = (bindings, neededImports, ns) => {
267
+ return (context) => {
268
+ return (sourceFile) => {
269
+ let ctx = {
270
+ bindings,
271
+ context,
272
+ factory: context.factory,
273
+ hasReactiveImport: false,
274
+ neededImports,
275
+ ns,
276
+ scopedBindings: []
277
+ };
278
+ return ts.visitNode(sourceFile, n => visit(ctx, n));
279
+ };
324
280
  };
325
- visit(ctx, sourceFile);
326
- if (ctx.replacements.length === 0) {
327
- return code;
328
- }
329
- let result = applyReplacements(code, ctx.replacements);
330
- if (ctx.neededImports.size > 0) {
331
- result = addMissingImports(result, ctx.neededImports);
332
- }
333
- return result;
334
281
  };
335
- export { transformReactivePrimitives };
282
+ export { createPrimitivesTransformer };
package/build/types.d.ts CHANGED
@@ -1,8 +1,12 @@
1
1
  import { COMPUTED, SIGNAL, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
2
2
  import { ReactiveArray, ReactiveObject } from './reactive/index.js';
3
- import { ts } from '@esportsplus/typescript';
4
3
  type BindingType = 'array' | 'computed' | 'object' | 'signal';
5
4
  type Bindings = Map<string, BindingType>;
5
+ interface Namespaces {
6
+ array: string;
7
+ constants: string;
8
+ reactivity: string;
9
+ }
6
10
  interface Computed<T> {
7
11
  cleanup: VoidFunction | VoidFunction[] | null;
8
12
  deps: Link | null;
@@ -31,9 +35,4 @@ type Signal<T> = {
31
35
  type: typeof SIGNAL;
32
36
  value: T;
33
37
  };
34
- interface TransformResult {
35
- code: string;
36
- sourceFile: ts.SourceFile;
37
- transformed: boolean;
38
- }
39
- export type { BindingType, Bindings, Computed, Link, ReactiveArray, ReactiveObject, Signal, TransformResult };
38
+ export type { BindingType, Bindings, Computed, Link, Namespaces, ReactiveArray, ReactiveObject, Signal };
package/package.json CHANGED
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "type": "module",
38
38
  "types": "build/index.d.ts",
39
- "version": "0.24.1",
39
+ "version": "0.24.3",
40
40
  "scripts": {
41
41
  "build": "tsc",
42
42
  "build:test": "pnpm build && vite build --config test/vite.config.ts",
@@ -0,0 +1,141 @@
1
+ import { ts } from '@esportsplus/typescript';
2
+
3
+
4
+ // Create: ns.read(expr)
5
+ function createReadCall(factory: ts.NodeFactory, ns: string, expr: ts.Expression): ts.CallExpression {
6
+ return factory.createCallExpression(
7
+ factory.createPropertyAccessExpression(factory.createIdentifier(ns), 'read'),
8
+ undefined,
9
+ [expr]
10
+ );
11
+ }
12
+
13
+ // Create: ns.set(target, value)
14
+ function createSetCall(factory: ts.NodeFactory, ns: string, target: ts.Expression, value: ts.Expression): ts.CallExpression {
15
+ return factory.createCallExpression(
16
+ factory.createPropertyAccessExpression(factory.createIdentifier(ns), 'set'),
17
+ undefined,
18
+ [target, value]
19
+ );
20
+ }
21
+
22
+ // Create: ns.signal(initialValue)
23
+ function createSignalCall(factory: ts.NodeFactory, ns: string, initialValue: ts.Expression): ts.CallExpression {
24
+ return factory.createCallExpression(
25
+ factory.createPropertyAccessExpression(factory.createIdentifier(ns), 'signal'),
26
+ undefined,
27
+ [initialValue]
28
+ );
29
+ }
30
+
31
+ // Create: ns.computed(fn)
32
+ function createComputedCall(factory: ts.NodeFactory, ns: string, fn: ts.Expression): ts.CallExpression {
33
+ return factory.createCallExpression(
34
+ factory.createPropertyAccessExpression(factory.createIdentifier(ns), 'computed'),
35
+ undefined,
36
+ [fn]
37
+ );
38
+ }
39
+
40
+ // Create: arr.$length()
41
+ function createArrayLengthCall(factory: ts.NodeFactory, arrayExpr: ts.Expression): ts.CallExpression {
42
+ return factory.createCallExpression(
43
+ factory.createPropertyAccessExpression(arrayExpr, '$length'),
44
+ undefined,
45
+ []
46
+ );
47
+ }
48
+
49
+ // Create: arr.$set(index, value)
50
+ function createArraySetCall(factory: ts.NodeFactory, arrayExpr: ts.Expression, index: ts.Expression, value: ts.Expression): ts.CallExpression {
51
+ return factory.createCallExpression(
52
+ factory.createPropertyAccessExpression(arrayExpr, '$set'),
53
+ undefined,
54
+ [index, value]
55
+ );
56
+ }
57
+
58
+ // Create: new ns.ReactiveArray(elements...)
59
+ function createReactiveArrayNew(factory: ts.NodeFactory, ns: string, elements: ts.Expression[]): ts.NewExpression {
60
+ return factory.createNewExpression(
61
+ factory.createPropertyAccessExpression(factory.createIdentifier(ns), 'ReactiveArray'),
62
+ undefined,
63
+ elements
64
+ );
65
+ }
66
+
67
+ // Create: ns.dispose(expr)
68
+ function createDisposeCall(factory: ts.NodeFactory, ns: string, expr: ts.Expression): ts.CallExpression {
69
+ return factory.createCallExpression(
70
+ factory.createPropertyAccessExpression(factory.createIdentifier(ns), 'dispose'),
71
+ undefined,
72
+ [expr]
73
+ );
74
+ }
75
+
76
+ // Create binary expression: left op right
77
+ function createBinaryExpr(factory: ts.NodeFactory, left: ts.Expression, op: ts.BinaryOperator, right: ts.Expression): ts.BinaryExpression {
78
+ return factory.createBinaryExpression(left, op, right);
79
+ }
80
+
81
+ // Create: (expr1, expr2) - comma expression
82
+ function createCommaExpr(factory: ts.NodeFactory, first: ts.Expression, second: ts.Expression): ts.ParenthesizedExpression {
83
+ return factory.createParenthesizedExpression(
84
+ factory.createBinaryExpression(first, ts.SyntaxKind.CommaToken, second)
85
+ );
86
+ }
87
+
88
+ // Create: ((tmp) => (ns.set(name, tmp op delta), tmp))(name.value)
89
+ function createPostfixIncrementExpr(
90
+ factory: ts.NodeFactory,
91
+ ns: string,
92
+ tmpName: string,
93
+ signalName: string,
94
+ op: ts.SyntaxKind.PlusToken | ts.SyntaxKind.MinusToken
95
+ ): ts.CallExpression {
96
+ let tmpIdent = factory.createIdentifier(tmpName),
97
+ signalIdent = factory.createIdentifier(signalName);
98
+
99
+ return factory.createCallExpression(
100
+ factory.createParenthesizedExpression(
101
+ factory.createArrowFunction(
102
+ undefined,
103
+ undefined,
104
+ [factory.createParameterDeclaration(undefined, undefined, tmpIdent)],
105
+ undefined,
106
+ factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
107
+ createCommaExpr(
108
+ factory,
109
+ createSetCall(
110
+ factory,
111
+ ns,
112
+ signalIdent,
113
+ factory.createBinaryExpression(
114
+ tmpIdent,
115
+ op,
116
+ factory.createNumericLiteral(1)
117
+ )
118
+ ),
119
+ tmpIdent
120
+ )
121
+ )
122
+ ),
123
+ undefined,
124
+ [factory.createPropertyAccessExpression(signalIdent, 'value')]
125
+ );
126
+ }
127
+
128
+
129
+ export {
130
+ createArrayLengthCall,
131
+ createArraySetCall,
132
+ createBinaryExpr,
133
+ createCommaExpr,
134
+ createComputedCall,
135
+ createDisposeCall,
136
+ createPostfixIncrementExpr,
137
+ createReactiveArrayNew,
138
+ createReadCall,
139
+ createSetCall,
140
+ createSignalCall
141
+ };