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