@esportsplus/reactivity 0.25.3 → 0.25.4

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.
Files changed (42) hide show
  1. package/build/constants.d.ts +8 -1
  2. package/build/constants.js +8 -1
  3. package/build/index.d.ts +2 -2
  4. package/build/index.js +2 -2
  5. package/build/reactive/array.js +18 -27
  6. package/build/reactive/index.d.ts +14 -13
  7. package/build/reactive/index.js +2 -1
  8. package/build/system.d.ts +2 -2
  9. package/build/system.js +10 -10
  10. package/build/transformer/detector.d.ts +2 -2
  11. package/build/transformer/detector.js +14 -14
  12. package/build/transformer/index.d.ts +3 -7
  13. package/build/transformer/index.js +21 -39
  14. package/build/transformer/plugins/tsc.js +7 -2
  15. package/build/transformer/plugins/vite.js +7 -9
  16. package/build/transformer/transforms/array.d.ts +3 -3
  17. package/build/transformer/transforms/array.js +16 -22
  18. package/build/transformer/transforms/object.d.ts +3 -3
  19. package/build/transformer/transforms/object.js +40 -56
  20. package/build/transformer/transforms/primitives.d.ts +3 -3
  21. package/build/transformer/transforms/primitives.js +54 -112
  22. package/build/types.d.ts +2 -2
  23. package/package.json +6 -6
  24. package/readme.md +5 -5
  25. package/src/constants.ts +20 -13
  26. package/src/index.ts +2 -2
  27. package/src/reactive/array.ts +18 -32
  28. package/src/reactive/index.ts +18 -15
  29. package/src/system.ts +14 -21
  30. package/src/transformer/detector.ts +16 -25
  31. package/src/transformer/index.ts +24 -46
  32. package/src/transformer/plugins/tsc.ts +8 -2
  33. package/src/transformer/plugins/vite.ts +9 -12
  34. package/src/transformer/transforms/array.ts +20 -37
  35. package/src/transformer/transforms/object.ts +54 -78
  36. package/src/transformer/transforms/primitives.ts +65 -140
  37. package/src/types.ts +5 -3
  38. package/test/vite.config.ts +1 -1
  39. package/build/transformer/transforms/utilities.d.ts +0 -8
  40. package/build/transformer/transforms/utilities.js +0 -27
  41. package/src/transformer/transforms/utilities.ts +0 -45
  42. package/storage/tsc-transform-analysis-2026-01-04.md +0 -125
@@ -1,15 +1,7 @@
1
- import { uid } from '@esportsplus/typescript/transformer';
2
- import type { Bindings } from '~/types';
3
- import { addMissingImports, applyReplacements, ExtraImport, Replacement } from './utilities';
4
1
  import { ts } from '@esportsplus/typescript';
5
-
6
-
7
- const CLASS_NAME_REGEX = /class (\w+)/;
8
-
9
- const EXTRA_IMPORTS: ExtraImport[] = [
10
- { module: '@esportsplus/reactivity/constants', specifier: 'REACTIVE_OBJECT' },
11
- { module: '@esportsplus/reactivity/reactive/array', specifier: 'ReactiveArray' }
12
- ];
2
+ import { code as c, type Replacement } from '@esportsplus/typescript/transformer';
3
+ import { COMPILATION_TYPE_ARRAY, COMPILATION_TYPE_COMPUTED, COMPILATION_TYPE_SIGNAL, PACKAGE } from '~/constants';
4
+ import type { Bindings } from '~/types';
13
5
 
14
6
 
15
7
  interface AnalyzedProperty {
@@ -19,19 +11,20 @@ interface AnalyzedProperty {
19
11
  }
20
12
 
21
13
  interface ReactiveObjectCall {
14
+ className: string;
22
15
  end: number;
23
- generatedClass: string;
24
- needsImports: Set<string>;
16
+ properties: AnalyzedProperty[];
25
17
  start: number;
26
18
  varName: string | null;
27
19
  }
28
20
 
29
21
  interface TransformContext {
30
- allNeededImports: Set<string>;
31
22
  bindings: Bindings;
32
23
  calls: ReactiveObjectCall[];
24
+ classCounter: number;
33
25
  hasReactiveImport: boolean;
34
26
  lastImportEnd: number;
27
+ ns: string;
35
28
  sourceFile: ts.SourceFile;
36
29
  }
37
30
 
@@ -54,43 +47,53 @@ function analyzeProperty(prop: ts.ObjectLiteralElementLike, sourceFile: ts.Sourc
54
47
  valueText = value.getText(sourceFile);
55
48
 
56
49
  if (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) {
57
- return { key, type: 'computed', valueText };
50
+ return { key, type: COMPILATION_TYPE_COMPUTED, valueText };
58
51
  }
59
52
 
60
53
  if (ts.isArrayLiteralExpression(value)) {
61
- return { key, type: 'array', valueText };
54
+ let elements = value.elements,
55
+ elementsText = '';
56
+
57
+ for (let i = 0, n = elements.length; i < n; i++) {
58
+ if (i > 0) {
59
+ elementsText += ', ';
60
+ }
61
+
62
+ elementsText += elements[i].getText(sourceFile);
63
+ }
64
+
65
+ return { key, type: COMPILATION_TYPE_ARRAY, valueText: elementsText };
62
66
  }
63
67
 
64
- return { key, type: 'signal', valueText };
68
+ return { key, type: COMPILATION_TYPE_SIGNAL, valueText };
65
69
  }
66
70
 
67
- function buildClassCode(className: string, properties: AnalyzedProperty[]): string {
71
+ function buildClassCode(className: string, properties: AnalyzedProperty[], ns: string): string {
68
72
  let accessors: string[] = [],
69
73
  disposeStatements: string[] = [],
70
- fields: string[] = [];
74
+ fields: string[] = [],
75
+ paramCounter = 0;
71
76
 
72
- fields.push(`[REACTIVE_OBJECT] = true;`);
77
+ fields.push(`[${ns}.REACTIVE_OBJECT] = true;`);
73
78
 
74
79
  for (let i = 0, n = properties.length; i < n; i++) {
75
80
  let { key, type, valueText } = properties[i];
76
81
 
77
- if (type === 'signal') {
78
- let param = uid('v');
82
+ if (type === COMPILATION_TYPE_SIGNAL) {
83
+ let param = `_v${paramCounter++}`;
79
84
 
80
- fields.push(`#${key} = signal(${valueText});`);
81
- accessors.push(`get ${key}() { return read(this.#${key}); }`);
82
- accessors.push(`set ${key}(${param}) { set(this.#${key}, ${param}); }`);
85
+ fields.push(`#${key} = ${ns}.signal(${valueText});`);
86
+ accessors.push(`get ${key}() { return ${ns}.read(this.#${key}); }`);
87
+ accessors.push(`set ${key}(${param}) { ${ns}.write(this.#${key}, ${param}); }`);
83
88
  }
84
- else if (type === 'array') {
85
- let elements = valueText.slice(1, -1);
86
-
87
- fields.push(`${key} = new ReactiveArray(${elements});`);
89
+ else if (type === COMPILATION_TYPE_ARRAY) {
90
+ fields.push(`${key} = new ${ns}.ReactiveArray(${valueText});`);
88
91
  disposeStatements.push(`this.${key}.dispose();`);
89
92
  }
90
- else if (type === 'computed') {
91
- fields.push(`#${key}: Computed<unknown> | null = null;`);
92
- accessors.push(`get ${key}() { return read(this.#${key} ??= computed(${valueText})); }`);
93
- disposeStatements.push(`if (this.#${key}) dispose(this.#${key});`);
93
+ else if (type === COMPILATION_TYPE_COMPUTED) {
94
+ fields.push(`#${key} = null;`);
95
+ accessors.push(`get ${key}() { return ${ns}.read(this.#${key} ??= ${ns}.computed(${valueText})); }`);
96
+ disposeStatements.push(`if (this.#${key}) ${ns}.dispose(this.#${key});`);
94
97
  }
95
98
  }
96
99
 
@@ -112,7 +115,7 @@ function visit(ctx: TransformContext, node: ts.Node): void {
112
115
 
113
116
  if (
114
117
  ts.isStringLiteral(node.moduleSpecifier) &&
115
- node.moduleSpecifier.text.includes('@esportsplus/reactivity')
118
+ node.moduleSpecifier.text.includes(PACKAGE)
116
119
  ) {
117
120
  let clause = node.importClause;
118
121
 
@@ -138,20 +141,15 @@ function visit(ctx: TransformContext, node: ts.Node): void {
138
141
  let arg = node.arguments[0];
139
142
 
140
143
  if (arg && ts.isObjectLiteralExpression(arg)) {
141
- let varName: string | null = null;
144
+ let properties: AnalyzedProperty[] = [],
145
+ props = arg.properties,
146
+ varName: string | null = null;
142
147
 
143
148
  if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
144
149
  varName = node.parent.name.text;
145
150
  ctx.bindings.set(varName, 'object');
146
151
  }
147
152
 
148
- let needsImports = new Set<string>(),
149
- properties: AnalyzedProperty[] = [];
150
-
151
- needsImports.add('REACTIVE_OBJECT');
152
-
153
- let props = arg.properties;
154
-
155
153
  for (let i = 0, n = props.length; i < n; i++) {
156
154
  let prop = props[i];
157
155
 
@@ -169,31 +167,15 @@ function visit(ctx: TransformContext, node: ts.Node): void {
169
167
 
170
168
  properties.push(analyzed);
171
169
 
172
- if (analyzed.type === 'signal') {
173
- needsImports.add('read');
174
- needsImports.add('set');
175
- needsImports.add('signal');
176
- }
177
- else if (analyzed.type === 'array') {
178
- needsImports.add('ReactiveArray');
179
-
180
- if (varName) {
181
- ctx.bindings.set(`${varName}.${analyzed.key}`, 'array');
182
- }
183
- }
184
- else if (analyzed.type === 'computed') {
185
- needsImports.add('computed');
186
- needsImports.add('dispose');
187
- needsImports.add('read');
170
+ if (analyzed.type === COMPILATION_TYPE_ARRAY && varName) {
171
+ ctx.bindings.set(`${varName}.${analyzed.key}`, COMPILATION_TYPE_ARRAY);
188
172
  }
189
173
  }
190
174
 
191
- needsImports.forEach(imp => ctx.allNeededImports.add(imp));
192
-
193
175
  ctx.calls.push({
176
+ className: `_RO${ctx.classCounter++}`,
194
177
  end: node.end,
195
- generatedClass: buildClassCode(uid('ReactiveObject'), properties),
196
- needsImports,
178
+ properties,
197
179
  start: node.pos,
198
180
  varName
199
181
  });
@@ -204,14 +186,15 @@ function visit(ctx: TransformContext, node: ts.Node): void {
204
186
  }
205
187
 
206
188
 
207
- const transformReactiveObjects = (sourceFile: ts.SourceFile, bindings: Bindings): string => {
189
+ export default (sourceFile: ts.SourceFile, bindings: Bindings, ns: string): string => {
208
190
  let code = sourceFile.getFullText(),
209
191
  ctx: TransformContext = {
210
- allNeededImports: new Set<string>(),
211
192
  bindings,
212
193
  calls: [],
194
+ classCounter: 0,
213
195
  hasReactiveImport: false,
214
196
  lastImportEnd: 0,
197
+ ns,
215
198
  sourceFile
216
199
  };
217
200
 
@@ -221,31 +204,24 @@ const transformReactiveObjects = (sourceFile: ts.SourceFile, bindings: Bindings)
221
204
  return code;
222
205
  }
223
206
 
224
- let replacements: Replacement[] = [];
207
+ let classes = ctx.calls.map(c => buildClassCode(c.className, c.properties, ns)).join('\n'),
208
+ replacements: Replacement[] = [];
225
209
 
226
210
  replacements.push({
227
211
  end: ctx.lastImportEnd,
228
- newText: code.substring(0, ctx.lastImportEnd) + '\n' + ctx.calls.map(c => c.generatedClass).join('\n') + '\n',
212
+ newText: code.substring(0, ctx.lastImportEnd) + '\n' + classes + '\n',
229
213
  start: 0
230
214
  });
231
215
 
232
216
  for (let i = 0, n = ctx.calls.length; i < n; i++) {
233
- let call = ctx.calls[i],
234
- classMatch = call.generatedClass.match(CLASS_NAME_REGEX);
217
+ let call = ctx.calls[i];
235
218
 
236
219
  replacements.push({
237
220
  end: call.end,
238
- newText: ` new ${classMatch ? classMatch[1] : 'ReactiveObject'}()`,
221
+ newText: ` new ${call.className}()`,
239
222
  start: call.start
240
223
  });
241
224
  }
242
225
 
243
- return addMissingImports(
244
- applyReplacements(code, replacements),
245
- ctx.allNeededImports,
246
- EXTRA_IMPORTS
247
- );
248
- };
249
-
250
-
251
- export { transformReactiveObjects };
226
+ return c.replace(code, replacements);
227
+ };
@@ -1,13 +1,13 @@
1
- import { uid, type Range } from '@esportsplus/typescript/transformer';
2
- import type { BindingType, Bindings } from '~/types';
3
- import { addMissingImports, applyReplacements, Replacement } from './utilities';
4
1
  import { ts } from '@esportsplus/typescript';
2
+ import { code as c, type Range, type Replacement } from '@esportsplus/typescript/transformer';
3
+ import type { BindingType, Bindings } from '~/types';
4
+ import { COMPILATION_TYPE_COMPUTED, COMPILATION_ENTRYPOINT, COMPILATION_TYPE_SIGNAL, PACKAGE } from '~/constants';
5
5
 
6
6
 
7
7
  interface ArgContext {
8
8
  argStart: number;
9
9
  innerReplacements: Replacement[];
10
- neededImports: Set<string>;
10
+ ns: string;
11
11
  scopedBindings: ScopeBinding[];
12
12
  sourceFile: ts.SourceFile;
13
13
  }
@@ -22,23 +22,43 @@ interface TransformContext {
22
22
  bindings: Bindings;
23
23
  computedArgRanges: Range[];
24
24
  hasReactiveImport: boolean;
25
- neededImports: Set<string>;
25
+ ns: string;
26
26
  replacements: Replacement[];
27
27
  scopedBindings: ScopeBinding[];
28
28
  sourceFile: ts.SourceFile;
29
+ tmpCounter: number;
29
30
  }
30
31
 
31
32
 
32
- function classifyReactiveArg(arg: ts.Expression): 'computed' | 'signal' | null {
33
+ let COMPOUND_OPERATORS = new Map<ts.SyntaxKind, string>([
34
+ [ts.SyntaxKind.AmpersandAmpersandEqualsToken, '&&'],
35
+ [ts.SyntaxKind.AmpersandEqualsToken, '&'],
36
+ [ts.SyntaxKind.AsteriskAsteriskEqualsToken, '**'],
37
+ [ts.SyntaxKind.AsteriskEqualsToken, '*'],
38
+ [ts.SyntaxKind.BarBarEqualsToken, '||'],
39
+ [ts.SyntaxKind.BarEqualsToken, '|'],
40
+ [ts.SyntaxKind.CaretEqualsToken, '^'],
41
+ [ts.SyntaxKind.GreaterThanGreaterThanEqualsToken, '>>'],
42
+ [ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, '>>>'],
43
+ [ts.SyntaxKind.LessThanLessThanEqualsToken, '<<'],
44
+ [ts.SyntaxKind.MinusEqualsToken, '-'],
45
+ [ts.SyntaxKind.PercentEqualsToken, '%'],
46
+ [ts.SyntaxKind.PlusEqualsToken, '+'],
47
+ [ts.SyntaxKind.QuestionQuestionEqualsToken, '??'],
48
+ [ts.SyntaxKind.SlashEqualsToken, '/']
49
+ ]);
50
+
51
+
52
+ function classifyReactiveArg(arg: ts.Expression): BindingType | null {
33
53
  if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
34
- return 'computed';
54
+ return COMPILATION_TYPE_COMPUTED;
35
55
  }
36
56
 
37
57
  if (ts.isObjectLiteralExpression(arg) || ts.isArrayLiteralExpression(arg)) {
38
58
  return null;
39
59
  }
40
60
 
41
- return 'signal';
61
+ return COMPILATION_TYPE_SIGNAL;
42
62
  }
43
63
 
44
64
  function findBinding(bindings: ScopeBinding[], name: string, node: ts.Node): ScopeBinding | undefined {
@@ -76,57 +96,6 @@ function findEnclosingScope(node: ts.Node): ts.Node {
76
96
  return node.getSourceFile();
77
97
  }
78
98
 
79
- function getCompoundOperator(kind: ts.SyntaxKind): string {
80
- if (kind === ts.SyntaxKind.PlusEqualsToken) {
81
- return '+';
82
- }
83
- else if (kind === ts.SyntaxKind.MinusEqualsToken) {
84
- return '-';
85
- }
86
- else if (kind === ts.SyntaxKind.AsteriskEqualsToken) {
87
- return '*';
88
- }
89
- else if (kind === ts.SyntaxKind.SlashEqualsToken) {
90
- return '/';
91
- }
92
- else if (kind === ts.SyntaxKind.PercentEqualsToken) {
93
- return '%';
94
- }
95
- else if (kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) {
96
- return '**';
97
- }
98
- else if (kind === ts.SyntaxKind.AmpersandEqualsToken) {
99
- return '&';
100
- }
101
- else if (kind === ts.SyntaxKind.BarEqualsToken) {
102
- return '|';
103
- }
104
- else if (kind === ts.SyntaxKind.CaretEqualsToken) {
105
- return '^';
106
- }
107
- else if (kind === ts.SyntaxKind.LessThanLessThanEqualsToken) {
108
- return '<<';
109
- }
110
- else if (kind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken) {
111
- return '>>';
112
- }
113
- else if (kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
114
- return '>>>';
115
- }
116
- else if (kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
117
- return '&&';
118
- }
119
- else if (kind === ts.SyntaxKind.BarBarEqualsToken) {
120
- return '||';
121
- }
122
- else if (kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
123
- return '??';
124
- }
125
- else {
126
- return '+';
127
- }
128
- }
129
-
130
99
  function isInComputedRange(ranges: Range[], start: number, end: number): boolean {
131
100
  for (let i = 0, n = ranges.length; i < n; i++) {
132
101
  let r = ranges[i];
@@ -142,11 +111,7 @@ function isInComputedRange(ranges: Range[], start: number, end: number): boolean
142
111
  function isInDeclarationInit(node: ts.Node): boolean {
143
112
  let parent = node.parent;
144
113
 
145
- if (ts.isVariableDeclaration(parent) && parent.initializer === node) {
146
- return true;
147
- }
148
-
149
- return false;
114
+ return ts.isVariableDeclaration(parent) && parent.initializer === node;
150
115
  }
151
116
 
152
117
  function isInScope(reference: ts.Node, binding: ScopeBinding): boolean {
@@ -172,7 +137,7 @@ function isReactiveReassignment(node: ts.Node): boolean {
172
137
  parent.right === node &&
173
138
  ts.isCallExpression(node) &&
174
139
  ts.isIdentifier((node as ts.CallExpression).expression) &&
175
- ((node as ts.CallExpression).expression as ts.Identifier).text === 'reactive'
140
+ ((node as ts.CallExpression).expression as ts.Identifier).text === COMPILATION_ENTRYPOINT
176
141
  ) {
177
142
  return true;
178
143
  }
@@ -190,15 +155,7 @@ function isWriteContext(node: ts.Identifier): 'simple' | 'compound' | 'increment
190
155
  return 'simple';
191
156
  }
192
157
 
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
- ) {
158
+ if (COMPOUND_OPERATORS.has(op)) {
202
159
  return 'compound';
203
160
  }
204
161
  }
@@ -218,13 +175,13 @@ function visit(ctx: TransformContext, node: ts.Node): void {
218
175
  if (
219
176
  ts.isImportDeclaration(node) &&
220
177
  ts.isStringLiteral(node.moduleSpecifier) &&
221
- node.moduleSpecifier.text.includes('@esportsplus/reactivity')
178
+ node.moduleSpecifier.text.includes(PACKAGE)
222
179
  ) {
223
180
  let clause = node.importClause;
224
181
 
225
182
  if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
226
183
  for (let i = 0, n = clause.namedBindings.elements.length; i < n; i++) {
227
- if (clause.namedBindings.elements[i].name.text === 'reactive') {
184
+ if (clause.namedBindings.elements[i].name.text === COMPILATION_ENTRYPOINT) {
228
185
  ctx.hasReactiveImport = true;
229
186
  break;
230
187
  }
@@ -236,7 +193,7 @@ function visit(ctx: TransformContext, node: ts.Node): void {
236
193
  ctx.hasReactiveImport &&
237
194
  ts.isCallExpression(node) &&
238
195
  ts.isIdentifier(node.expression) &&
239
- node.expression.text === 'reactive' &&
196
+ node.expression.text === COMPILATION_ENTRYPOINT &&
240
197
  node.arguments.length > 0
241
198
  ) {
242
199
  let arg = node.arguments[0],
@@ -264,42 +221,35 @@ function visit(ctx: TransformContext, node: ts.Node): void {
264
221
  ctx.bindings.set(varName, classification);
265
222
  }
266
223
 
267
- if (classification === 'computed') {
268
- ctx.computedArgRanges.push({
269
- end: arg.end,
270
- start: arg.getStart(ctx.sourceFile)
271
- });
224
+ if (classification === COMPILATION_TYPE_COMPUTED) {
225
+ let argStart = arg.getStart(ctx.sourceFile);
226
+
227
+ ctx.computedArgRanges.push({ end: arg.end, start: argStart });
272
228
 
273
229
  let argCtx: ArgContext = {
274
- argStart: arg.getStart(ctx.sourceFile),
230
+ argStart,
275
231
  innerReplacements: [],
276
- neededImports: ctx.neededImports,
232
+ ns: ctx.ns,
277
233
  scopedBindings: ctx.scopedBindings,
278
234
  sourceFile: ctx.sourceFile
279
235
  };
280
236
 
281
237
  visitArg(argCtx, arg);
282
238
 
283
- let argText = applyReplacements(arg.getText(ctx.sourceFile), argCtx.innerReplacements);
239
+ let argText = c.replace(arg.getText(ctx.sourceFile), argCtx.innerReplacements);
284
240
 
285
241
  ctx.replacements.push({
286
242
  end: node.end,
287
- newText: `computed(${argText})`,
243
+ newText: `${ctx.ns}.computed(${argText})`,
288
244
  start: node.pos
289
245
  });
290
-
291
- ctx.neededImports.add('computed');
292
246
  }
293
247
  else {
294
- let argText = arg.getText(ctx.sourceFile);
295
-
296
248
  ctx.replacements.push({
297
249
  end: node.end,
298
- newText: `signal(${argText})`,
250
+ newText: `${ctx.ns}.signal(${arg.getText(ctx.sourceFile)})`,
299
251
  start: node.pos
300
252
  });
301
-
302
- ctx.neededImports.add('signal');
303
253
  }
304
254
  }
305
255
  }
@@ -328,55 +278,49 @@ function visit(ctx: TransformContext, node: ts.Node): void {
328
278
  let writeCtx = isWriteContext(node);
329
279
 
330
280
  if (writeCtx) {
331
- if (binding.type !== 'computed') {
332
- ctx.neededImports.add('set');
333
-
281
+ if (binding.type !== COMPILATION_TYPE_COMPUTED) {
334
282
  let parent = node.parent;
335
283
 
336
284
  if (writeCtx === 'simple' && ts.isBinaryExpression(parent)) {
337
- let valueText = parent.right.getText(ctx.sourceFile);
338
-
339
285
  ctx.replacements.push({
340
286
  end: parent.end,
341
- newText: `set(${name}, ${valueText})`,
287
+ newText: `${ctx.ns}.write(${name}, ${parent.right.getText(ctx.sourceFile)})`,
342
288
  start: parent.pos
343
289
  });
344
290
  }
345
291
  else if (writeCtx === 'compound' && ts.isBinaryExpression(parent)) {
346
- let op = getCompoundOperator(parent.operatorToken.kind),
347
- valueText = parent.right.getText(ctx.sourceFile);
292
+ let op = COMPOUND_OPERATORS.get(parent.operatorToken.kind) ?? '+';
348
293
 
349
294
  ctx.replacements.push({
350
295
  end: parent.end,
351
- newText: `set(${name}, ${name}.value ${op} ${valueText})`,
296
+ newText: `${ctx.ns}.write(${name}, ${name}.value ${op} ${parent.right.getText(ctx.sourceFile)})`,
352
297
  start: parent.pos
353
298
  });
354
299
  }
355
300
  else if (writeCtx === 'increment') {
356
- let isPrefix = ts.isPrefixUnaryExpression(parent),
357
- op = (parent as ts.PrefixUnaryExpression | ts.PostfixUnaryExpression).operator,
358
- delta = op === ts.SyntaxKind.PlusPlusToken ? '+ 1' : '- 1';
301
+ let delta = (parent as ts.PrefixUnaryExpression | ts.PostfixUnaryExpression).operator === ts.SyntaxKind.PlusPlusToken ? '+ 1' : '- 1',
302
+ isPrefix = ts.isPrefixUnaryExpression(parent);
359
303
 
360
304
  if (ts.isExpressionStatement(parent.parent)) {
361
305
  ctx.replacements.push({
362
306
  end: parent.end,
363
- newText: `set(${name}, ${name}.value ${delta})`,
307
+ newText: `${ctx.ns}.write(${name}, ${name}.value ${delta})`,
364
308
  start: parent.pos
365
309
  });
366
310
  }
367
311
  else if (isPrefix) {
368
312
  ctx.replacements.push({
369
313
  end: parent.end,
370
- newText: `(set(${name}, ${name}.value ${delta}), ${name}.value)`,
314
+ newText: `(${ctx.ns}.write(${name}, ${name}.value ${delta}), ${name}.value)`,
371
315
  start: parent.pos
372
316
  });
373
317
  }
374
318
  else {
375
- let tmp = uid('tmp');
319
+ let tmp = `_t${ctx.tmpCounter++}`;
376
320
 
377
321
  ctx.replacements.push({
378
322
  end: parent.end,
379
- newText: `((${tmp}) => (set(${name}, ${tmp} ${delta}), ${tmp}))(${name}.value)`,
323
+ newText: `((${tmp}) => (${ctx.ns}.write(${name}, ${tmp} ${delta}), ${tmp}))(${name}.value)`,
380
324
  start: parent.pos
381
325
  });
382
326
  }
@@ -384,11 +328,9 @@ function visit(ctx: TransformContext, node: ts.Node): void {
384
328
  }
385
329
  }
386
330
  else {
387
- ctx.neededImports.add('read');
388
-
389
331
  ctx.replacements.push({
390
332
  end: node.end,
391
- newText: `read(${name})`,
333
+ newText: `${ctx.ns}.read(${name})`,
392
334
  start: node.pos
393
335
  });
394
336
  }
@@ -401,24 +343,18 @@ function visit(ctx: TransformContext, node: ts.Node): void {
401
343
 
402
344
  function visitArg(ctx: ArgContext, node: ts.Node): void {
403
345
  if (ts.isIdentifier(node) && node.parent) {
404
- if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
405
- ts.forEachChild(node, n => visitArg(ctx, n));
406
- return;
407
- }
408
-
409
- if (ts.isCallExpression(node.parent) && node.parent.expression === node) {
346
+ if (
347
+ (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) ||
348
+ (ts.isCallExpression(node.parent) && node.parent.expression === node)
349
+ ) {
410
350
  ts.forEachChild(node, n => visitArg(ctx, n));
411
351
  return;
412
352
  }
413
353
 
414
- let binding = findBinding(ctx.scopedBindings, node.text, node);
415
-
416
- if (binding) {
417
- ctx.neededImports.add('read');
418
-
354
+ if (findBinding(ctx.scopedBindings, node.text, node)) {
419
355
  ctx.innerReplacements.push({
420
356
  end: node.end - ctx.argStart,
421
- newText: `read(${node.text})`,
357
+ newText: `${ctx.ns}.read(${node.text})`,
422
358
  start: node.getStart(ctx.sourceFile) - ctx.argStart
423
359
  });
424
360
  }
@@ -428,19 +364,17 @@ function visitArg(ctx: ArgContext, node: ts.Node): void {
428
364
  }
429
365
 
430
366
 
431
- const transformReactivePrimitives = (
432
- sourceFile: ts.SourceFile,
433
- bindings: Bindings
434
- ): string => {
367
+ export default (sourceFile: ts.SourceFile, bindings: Bindings, ns: string): string => {
435
368
  let code = sourceFile.getFullText(),
436
369
  ctx: TransformContext = {
437
370
  bindings,
438
371
  computedArgRanges: [],
439
372
  hasReactiveImport: false,
440
- neededImports: new Set<string>(),
373
+ ns,
441
374
  replacements: [],
442
375
  scopedBindings: [],
443
- sourceFile
376
+ sourceFile,
377
+ tmpCounter: 0
444
378
  };
445
379
 
446
380
  visit(ctx, sourceFile);
@@ -449,14 +383,5 @@ const transformReactivePrimitives = (
449
383
  return code;
450
384
  }
451
385
 
452
- let result = applyReplacements(code, ctx.replacements);
453
-
454
- if (ctx.neededImports.size > 0) {
455
- result = addMissingImports(result, ctx.neededImports);
456
- }
457
-
458
- return result;
386
+ return c.replace(code, ctx.replacements);
459
387
  };
460
-
461
-
462
- export { transformReactivePrimitives };
package/src/types.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { COMPUTED, SIGNAL, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants';
2
- import { ReactiveArray, ReactiveObject } from './reactive';
3
2
  import { ts } from '@esportsplus/typescript';
4
3
 
5
4
 
@@ -36,6 +35,10 @@ interface Link {
36
35
  version: number;
37
36
  }
38
37
 
38
+ // If we expose internals optimizing compiler may break api.
39
+ // Instead we will use this as a shim.
40
+ type Reactive<T> = T;
41
+
39
42
  type Signal<T> = {
40
43
  subs: Link | null;
41
44
  subsTail: Link | null;
@@ -55,8 +58,7 @@ export type {
55
58
  Bindings,
56
59
  Computed,
57
60
  Link,
58
- ReactiveArray,
59
- ReactiveObject,
61
+ Reactive,
60
62
  Signal,
61
63
  TransformResult
62
64
  };
@@ -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 };