@esportsplus/reactivity 0.25.3 → 0.25.5

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 +9 -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 +20 -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 +22 -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 +22 -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,11 +1,6 @@
1
- import { uid } from '@esportsplus/typescript/transformer';
2
- import { addMissingImports, applyReplacements } from './utilities.js';
3
1
  import { ts } from '@esportsplus/typescript';
4
- const CLASS_NAME_REGEX = /class (\w+)/;
5
- const EXTRA_IMPORTS = [
6
- { module: '@esportsplus/reactivity/constants', specifier: 'REACTIVE_OBJECT' },
7
- { module: '@esportsplus/reactivity/reactive/array', specifier: 'ReactiveArray' }
8
- ];
2
+ import { code as c } from '@esportsplus/typescript/transformer';
3
+ import { COMPILATION_TYPE_ARRAY, COMPILATION_TYPE_COMPUTED, COMPILATION_TYPE_SIGNAL, PACKAGE } from '../../constants.js';
9
4
  function analyzeProperty(prop, sourceFile) {
10
5
  if (!ts.isPropertyAssignment(prop)) {
11
6
  return null;
@@ -19,33 +14,39 @@ function analyzeProperty(prop, sourceFile) {
19
14
  }
20
15
  let value = prop.initializer, valueText = value.getText(sourceFile);
21
16
  if (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) {
22
- return { key, type: 'computed', valueText };
17
+ return { key, type: COMPILATION_TYPE_COMPUTED, valueText };
23
18
  }
24
19
  if (ts.isArrayLiteralExpression(value)) {
25
- return { key, type: 'array', valueText };
20
+ let elements = value.elements, elementsText = '';
21
+ for (let i = 0, n = elements.length; i < n; i++) {
22
+ if (i > 0) {
23
+ elementsText += ', ';
24
+ }
25
+ elementsText += elements[i].getText(sourceFile);
26
+ }
27
+ return { key, type: COMPILATION_TYPE_ARRAY, valueText: elementsText };
26
28
  }
27
- return { key, type: 'signal', valueText };
29
+ return { key, type: COMPILATION_TYPE_SIGNAL, valueText };
28
30
  }
29
- function buildClassCode(className, properties) {
30
- let accessors = [], disposeStatements = [], fields = [];
31
- fields.push(`[REACTIVE_OBJECT] = true;`);
31
+ function buildClassCode(className, properties, ns) {
32
+ let accessors = [], disposeStatements = [], fields = [], paramCounter = 0;
33
+ fields.push(`[${ns}.REACTIVE_OBJECT] = true;`);
32
34
  for (let i = 0, n = properties.length; i < n; i++) {
33
35
  let { key, type, valueText } = properties[i];
34
- if (type === 'signal') {
35
- let param = uid('v');
36
- fields.push(`#${key} = signal(${valueText});`);
37
- accessors.push(`get ${key}() { return read(this.#${key}); }`);
38
- accessors.push(`set ${key}(${param}) { set(this.#${key}, ${param}); }`);
36
+ if (type === COMPILATION_TYPE_SIGNAL) {
37
+ let param = `_v${paramCounter++}`;
38
+ fields.push(`#${key} = ${ns}.signal(${valueText});`);
39
+ accessors.push(`get ${key}() { return ${ns}.read(this.#${key}); }`);
40
+ accessors.push(`set ${key}(${param}) { ${ns}.write(this.#${key}, ${param}); }`);
39
41
  }
40
- else if (type === 'array') {
41
- let elements = valueText.slice(1, -1);
42
- fields.push(`${key} = new ReactiveArray(${elements});`);
42
+ else if (type === COMPILATION_TYPE_ARRAY) {
43
+ fields.push(`${key} = new ${ns}.ReactiveArray(${valueText});`);
43
44
  disposeStatements.push(`this.${key}.dispose();`);
44
45
  }
45
- else if (type === 'computed') {
46
- fields.push(`#${key}: Computed<unknown> | null = null;`);
47
- accessors.push(`get ${key}() { return read(this.#${key} ??= computed(${valueText})); }`);
48
- disposeStatements.push(`if (this.#${key}) dispose(this.#${key});`);
46
+ else if (type === COMPILATION_TYPE_COMPUTED) {
47
+ fields.push(`#${key} = null;`);
48
+ accessors.push(`get ${key}() { return ${ns}.read(this.#${key} ??= ${ns}.computed(${valueText})); }`);
49
+ disposeStatements.push(`if (this.#${key}) ${ns}.dispose(this.#${key});`);
49
50
  }
50
51
  }
51
52
  return `
@@ -63,7 +64,7 @@ function visit(ctx, node) {
63
64
  if (ts.isImportDeclaration(node)) {
64
65
  ctx.lastImportEnd = node.end;
65
66
  if (ts.isStringLiteral(node.moduleSpecifier) &&
66
- node.moduleSpecifier.text.includes('@esportsplus/reactivity')) {
67
+ node.moduleSpecifier.text.includes(PACKAGE)) {
67
68
  let clause = node.importClause;
68
69
  if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
69
70
  let elements = clause.namedBindings.elements;
@@ -82,14 +83,11 @@ function visit(ctx, node) {
82
83
  node.expression.text === 'reactive') {
83
84
  let arg = node.arguments[0];
84
85
  if (arg && ts.isObjectLiteralExpression(arg)) {
85
- let varName = null;
86
+ let properties = [], props = arg.properties, varName = null;
86
87
  if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
87
88
  varName = node.parent.name.text;
88
89
  ctx.bindings.set(varName, 'object');
89
90
  }
90
- let needsImports = new Set(), properties = [];
91
- needsImports.add('REACTIVE_OBJECT');
92
- let props = arg.properties;
93
91
  for (let i = 0, n = props.length; i < n; i++) {
94
92
  let prop = props[i];
95
93
  if (ts.isSpreadAssignment(prop)) {
@@ -102,28 +100,14 @@ function visit(ctx, node) {
102
100
  return;
103
101
  }
104
102
  properties.push(analyzed);
105
- if (analyzed.type === 'signal') {
106
- needsImports.add('read');
107
- needsImports.add('set');
108
- needsImports.add('signal');
109
- }
110
- else if (analyzed.type === 'array') {
111
- needsImports.add('ReactiveArray');
112
- if (varName) {
113
- ctx.bindings.set(`${varName}.${analyzed.key}`, 'array');
114
- }
115
- }
116
- else if (analyzed.type === 'computed') {
117
- needsImports.add('computed');
118
- needsImports.add('dispose');
119
- needsImports.add('read');
103
+ if (analyzed.type === COMPILATION_TYPE_ARRAY && varName) {
104
+ ctx.bindings.set(`${varName}.${analyzed.key}`, COMPILATION_TYPE_ARRAY);
120
105
  }
121
106
  }
122
- needsImports.forEach(imp => ctx.allNeededImports.add(imp));
123
107
  ctx.calls.push({
108
+ className: `_RO${ctx.classCounter++}`,
124
109
  end: node.end,
125
- generatedClass: buildClassCode(uid('ReactiveObject'), properties),
126
- needsImports,
110
+ properties,
127
111
  start: node.pos,
128
112
  varName
129
113
  });
@@ -131,33 +115,33 @@ function visit(ctx, node) {
131
115
  }
132
116
  ts.forEachChild(node, n => visit(ctx, n));
133
117
  }
134
- const transformReactiveObjects = (sourceFile, bindings) => {
118
+ export default (sourceFile, bindings, ns) => {
135
119
  let code = sourceFile.getFullText(), ctx = {
136
- allNeededImports: new Set(),
137
120
  bindings,
138
121
  calls: [],
122
+ classCounter: 0,
139
123
  hasReactiveImport: false,
140
124
  lastImportEnd: 0,
125
+ ns,
141
126
  sourceFile
142
127
  };
143
128
  visit(ctx, sourceFile);
144
129
  if (ctx.calls.length === 0) {
145
130
  return code;
146
131
  }
147
- let replacements = [];
132
+ let classes = ctx.calls.map(c => buildClassCode(c.className, c.properties, ns)).join('\n'), replacements = [];
148
133
  replacements.push({
149
134
  end: ctx.lastImportEnd,
150
- newText: code.substring(0, ctx.lastImportEnd) + '\n' + ctx.calls.map(c => c.generatedClass).join('\n') + '\n',
135
+ newText: code.substring(0, ctx.lastImportEnd) + '\n' + classes + '\n',
151
136
  start: 0
152
137
  });
153
138
  for (let i = 0, n = ctx.calls.length; i < n; i++) {
154
- let call = ctx.calls[i], classMatch = call.generatedClass.match(CLASS_NAME_REGEX);
139
+ let call = ctx.calls[i];
155
140
  replacements.push({
156
141
  end: call.end,
157
- newText: ` new ${classMatch ? classMatch[1] : 'ReactiveObject'}()`,
142
+ newText: ` new ${call.className}()`,
158
143
  start: call.start
159
144
  });
160
145
  }
161
- return addMissingImports(applyReplacements(code, replacements), ctx.allNeededImports, EXTRA_IMPORTS);
146
+ return c.replace(code, replacements);
162
147
  };
163
- export { transformReactiveObjects };
@@ -1,4 +1,4 @@
1
- import type { Bindings } from '../../types.js';
2
1
  import { ts } from '@esportsplus/typescript';
3
- declare const transformReactivePrimitives: (sourceFile: ts.SourceFile, bindings: Bindings) => string;
4
- export { transformReactivePrimitives };
2
+ import type { Bindings } from '../../types.js';
3
+ declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings, ns: string) => string;
4
+ export default _default;
@@ -1,14 +1,31 @@
1
- import { uid } from '@esportsplus/typescript/transformer';
2
- import { addMissingImports, applyReplacements } from './utilities.js';
3
1
  import { ts } from '@esportsplus/typescript';
2
+ import { code as c } from '@esportsplus/typescript/transformer';
3
+ import { COMPILATION_TYPE_COMPUTED, COMPILATION_ENTRYPOINT, COMPILATION_TYPE_SIGNAL, PACKAGE } from '../../constants.js';
4
+ let COMPOUND_OPERATORS = new Map([
5
+ [ts.SyntaxKind.AmpersandAmpersandEqualsToken, '&&'],
6
+ [ts.SyntaxKind.AmpersandEqualsToken, '&'],
7
+ [ts.SyntaxKind.AsteriskAsteriskEqualsToken, '**'],
8
+ [ts.SyntaxKind.AsteriskEqualsToken, '*'],
9
+ [ts.SyntaxKind.BarBarEqualsToken, '||'],
10
+ [ts.SyntaxKind.BarEqualsToken, '|'],
11
+ [ts.SyntaxKind.CaretEqualsToken, '^'],
12
+ [ts.SyntaxKind.GreaterThanGreaterThanEqualsToken, '>>'],
13
+ [ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, '>>>'],
14
+ [ts.SyntaxKind.LessThanLessThanEqualsToken, '<<'],
15
+ [ts.SyntaxKind.MinusEqualsToken, '-'],
16
+ [ts.SyntaxKind.PercentEqualsToken, '%'],
17
+ [ts.SyntaxKind.PlusEqualsToken, '+'],
18
+ [ts.SyntaxKind.QuestionQuestionEqualsToken, '??'],
19
+ [ts.SyntaxKind.SlashEqualsToken, '/']
20
+ ]);
4
21
  function classifyReactiveArg(arg) {
5
22
  if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
6
- return 'computed';
23
+ return COMPILATION_TYPE_COMPUTED;
7
24
  }
8
25
  if (ts.isObjectLiteralExpression(arg) || ts.isArrayLiteralExpression(arg)) {
9
26
  return null;
10
27
  }
11
- return 'signal';
28
+ return COMPILATION_TYPE_SIGNAL;
12
29
  }
13
30
  function findBinding(bindings, name, node) {
14
31
  for (let i = 0, n = bindings.length; i < n; i++) {
@@ -36,56 +53,6 @@ function findEnclosingScope(node) {
36
53
  }
37
54
  return node.getSourceFile();
38
55
  }
39
- function getCompoundOperator(kind) {
40
- if (kind === ts.SyntaxKind.PlusEqualsToken) {
41
- return '+';
42
- }
43
- else if (kind === ts.SyntaxKind.MinusEqualsToken) {
44
- return '-';
45
- }
46
- else if (kind === ts.SyntaxKind.AsteriskEqualsToken) {
47
- return '*';
48
- }
49
- else if (kind === ts.SyntaxKind.SlashEqualsToken) {
50
- return '/';
51
- }
52
- else if (kind === ts.SyntaxKind.PercentEqualsToken) {
53
- return '%';
54
- }
55
- else if (kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) {
56
- return '**';
57
- }
58
- else if (kind === ts.SyntaxKind.AmpersandEqualsToken) {
59
- return '&';
60
- }
61
- else if (kind === ts.SyntaxKind.BarEqualsToken) {
62
- return '|';
63
- }
64
- else if (kind === ts.SyntaxKind.CaretEqualsToken) {
65
- return '^';
66
- }
67
- else if (kind === ts.SyntaxKind.LessThanLessThanEqualsToken) {
68
- return '<<';
69
- }
70
- else if (kind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken) {
71
- return '>>';
72
- }
73
- else if (kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
74
- return '>>>';
75
- }
76
- else if (kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
77
- return '&&';
78
- }
79
- else if (kind === ts.SyntaxKind.BarBarEqualsToken) {
80
- return '||';
81
- }
82
- else if (kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
83
- return '??';
84
- }
85
- else {
86
- return '+';
87
- }
88
- }
89
56
  function isInComputedRange(ranges, start, end) {
90
57
  for (let i = 0, n = ranges.length; i < n; i++) {
91
58
  let r = ranges[i];
@@ -97,10 +64,7 @@ function isInComputedRange(ranges, start, end) {
97
64
  }
98
65
  function isInDeclarationInit(node) {
99
66
  let parent = node.parent;
100
- if (ts.isVariableDeclaration(parent) && parent.initializer === node) {
101
- return true;
102
- }
103
- return false;
67
+ return ts.isVariableDeclaration(parent) && parent.initializer === node;
104
68
  }
105
69
  function isInScope(reference, binding) {
106
70
  let current = reference;
@@ -119,7 +83,7 @@ function isReactiveReassignment(node) {
119
83
  parent.right === node &&
120
84
  ts.isCallExpression(node) &&
121
85
  ts.isIdentifier(node.expression) &&
122
- node.expression.text === 'reactive') {
86
+ node.expression.text === COMPILATION_ENTRYPOINT) {
123
87
  return true;
124
88
  }
125
89
  return false;
@@ -131,12 +95,7 @@ function isWriteContext(node) {
131
95
  if (op === ts.SyntaxKind.EqualsToken) {
132
96
  return 'simple';
133
97
  }
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) {
98
+ if (COMPOUND_OPERATORS.has(op)) {
140
99
  return 'compound';
141
100
  }
142
101
  }
@@ -151,11 +110,11 @@ function isWriteContext(node) {
151
110
  function visit(ctx, node) {
152
111
  if (ts.isImportDeclaration(node) &&
153
112
  ts.isStringLiteral(node.moduleSpecifier) &&
154
- node.moduleSpecifier.text.includes('@esportsplus/reactivity')) {
113
+ node.moduleSpecifier.text.includes(PACKAGE)) {
155
114
  let clause = node.importClause;
156
115
  if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
157
116
  for (let i = 0, n = clause.namedBindings.elements.length; i < n; i++) {
158
- if (clause.namedBindings.elements[i].name.text === 'reactive') {
117
+ if (clause.namedBindings.elements[i].name.text === COMPILATION_ENTRYPOINT) {
159
118
  ctx.hasReactiveImport = true;
160
119
  break;
161
120
  }
@@ -165,7 +124,7 @@ function visit(ctx, node) {
165
124
  if (ctx.hasReactiveImport &&
166
125
  ts.isCallExpression(node) &&
167
126
  ts.isIdentifier(node.expression) &&
168
- node.expression.text === 'reactive' &&
127
+ node.expression.text === COMPILATION_ENTRYPOINT &&
169
128
  node.arguments.length > 0) {
170
129
  let arg = node.arguments[0], classification = classifyReactiveArg(arg);
171
130
  if (classification) {
@@ -184,35 +143,30 @@ function visit(ctx, node) {
184
143
  ctx.scopedBindings.push({ name: varName, scope, type: classification });
185
144
  ctx.bindings.set(varName, classification);
186
145
  }
187
- if (classification === 'computed') {
188
- ctx.computedArgRanges.push({
189
- end: arg.end,
190
- start: arg.getStart(ctx.sourceFile)
191
- });
146
+ if (classification === COMPILATION_TYPE_COMPUTED) {
147
+ let argStart = arg.getStart(ctx.sourceFile);
148
+ ctx.computedArgRanges.push({ end: arg.end, start: argStart });
192
149
  let argCtx = {
193
- argStart: arg.getStart(ctx.sourceFile),
150
+ argStart,
194
151
  innerReplacements: [],
195
- neededImports: ctx.neededImports,
152
+ ns: ctx.ns,
196
153
  scopedBindings: ctx.scopedBindings,
197
154
  sourceFile: ctx.sourceFile
198
155
  };
199
156
  visitArg(argCtx, arg);
200
- let argText = applyReplacements(arg.getText(ctx.sourceFile), argCtx.innerReplacements);
157
+ let argText = c.replace(arg.getText(ctx.sourceFile), argCtx.innerReplacements);
201
158
  ctx.replacements.push({
202
159
  end: node.end,
203
- newText: `computed(${argText})`,
160
+ newText: `${ctx.ns}.computed(${argText})`,
204
161
  start: node.pos
205
162
  });
206
- ctx.neededImports.add('computed');
207
163
  }
208
164
  else {
209
- let argText = arg.getText(ctx.sourceFile);
210
165
  ctx.replacements.push({
211
166
  end: node.end,
212
- newText: `signal(${argText})`,
167
+ newText: `${ctx.ns}.signal(${arg.getText(ctx.sourceFile)})`,
213
168
  start: node.pos
214
169
  });
215
- ctx.neededImports.add('signal');
216
170
  }
217
171
  }
218
172
  }
@@ -232,46 +186,44 @@ function visit(ctx, node) {
232
186
  !(ts.isTypeOfExpression(node.parent) && node.parent.expression === node)) {
233
187
  let writeCtx = isWriteContext(node);
234
188
  if (writeCtx) {
235
- if (binding.type !== 'computed') {
236
- ctx.neededImports.add('set');
189
+ if (binding.type !== COMPILATION_TYPE_COMPUTED) {
237
190
  let parent = node.parent;
238
191
  if (writeCtx === 'simple' && ts.isBinaryExpression(parent)) {
239
- let valueText = parent.right.getText(ctx.sourceFile);
240
192
  ctx.replacements.push({
241
193
  end: parent.end,
242
- newText: `set(${name}, ${valueText})`,
194
+ newText: `${ctx.ns}.write(${name}, ${parent.right.getText(ctx.sourceFile)})`,
243
195
  start: parent.pos
244
196
  });
245
197
  }
246
198
  else if (writeCtx === 'compound' && ts.isBinaryExpression(parent)) {
247
- let op = getCompoundOperator(parent.operatorToken.kind), valueText = parent.right.getText(ctx.sourceFile);
199
+ let op = COMPOUND_OPERATORS.get(parent.operatorToken.kind) ?? '+';
248
200
  ctx.replacements.push({
249
201
  end: parent.end,
250
- newText: `set(${name}, ${name}.value ${op} ${valueText})`,
202
+ newText: `${ctx.ns}.write(${name}, ${name}.value ${op} ${parent.right.getText(ctx.sourceFile)})`,
251
203
  start: parent.pos
252
204
  });
253
205
  }
254
206
  else if (writeCtx === 'increment') {
255
- let isPrefix = ts.isPrefixUnaryExpression(parent), op = parent.operator, delta = op === ts.SyntaxKind.PlusPlusToken ? '+ 1' : '- 1';
207
+ let delta = parent.operator === ts.SyntaxKind.PlusPlusToken ? '+ 1' : '- 1', isPrefix = ts.isPrefixUnaryExpression(parent);
256
208
  if (ts.isExpressionStatement(parent.parent)) {
257
209
  ctx.replacements.push({
258
210
  end: parent.end,
259
- newText: `set(${name}, ${name}.value ${delta})`,
211
+ newText: `${ctx.ns}.write(${name}, ${name}.value ${delta})`,
260
212
  start: parent.pos
261
213
  });
262
214
  }
263
215
  else if (isPrefix) {
264
216
  ctx.replacements.push({
265
217
  end: parent.end,
266
- newText: `(set(${name}, ${name}.value ${delta}), ${name}.value)`,
218
+ newText: `(${ctx.ns}.write(${name}, ${name}.value ${delta}), ${name}.value)`,
267
219
  start: parent.pos
268
220
  });
269
221
  }
270
222
  else {
271
- let tmp = uid('tmp');
223
+ let tmp = `_t${ctx.tmpCounter++}`;
272
224
  ctx.replacements.push({
273
225
  end: parent.end,
274
- newText: `((${tmp}) => (set(${name}, ${tmp} ${delta}), ${tmp}))(${name}.value)`,
226
+ newText: `((${tmp}) => (${ctx.ns}.write(${name}, ${tmp} ${delta}), ${tmp}))(${name}.value)`,
275
227
  start: parent.pos
276
228
  });
277
229
  }
@@ -279,10 +231,9 @@ function visit(ctx, node) {
279
231
  }
280
232
  }
281
233
  else {
282
- ctx.neededImports.add('read');
283
234
  ctx.replacements.push({
284
235
  end: node.end,
285
- newText: `read(${name})`,
236
+ newText: `${ctx.ns}.read(${name})`,
286
237
  start: node.pos
287
238
  });
288
239
  }
@@ -293,44 +244,35 @@ function visit(ctx, node) {
293
244
  }
294
245
  function visitArg(ctx, node) {
295
246
  if (ts.isIdentifier(node) && node.parent) {
296
- if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
247
+ if ((ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) ||
248
+ (ts.isCallExpression(node.parent) && node.parent.expression === node)) {
297
249
  ts.forEachChild(node, n => visitArg(ctx, n));
298
250
  return;
299
251
  }
300
- if (ts.isCallExpression(node.parent) && node.parent.expression === node) {
301
- ts.forEachChild(node, n => visitArg(ctx, n));
302
- return;
303
- }
304
- let binding = findBinding(ctx.scopedBindings, node.text, node);
305
- if (binding) {
306
- ctx.neededImports.add('read');
252
+ if (findBinding(ctx.scopedBindings, node.text, node)) {
307
253
  ctx.innerReplacements.push({
308
254
  end: node.end - ctx.argStart,
309
- newText: `read(${node.text})`,
255
+ newText: `${ctx.ns}.read(${node.text})`,
310
256
  start: node.getStart(ctx.sourceFile) - ctx.argStart
311
257
  });
312
258
  }
313
259
  }
314
260
  ts.forEachChild(node, n => visitArg(ctx, n));
315
261
  }
316
- const transformReactivePrimitives = (sourceFile, bindings) => {
262
+ export default (sourceFile, bindings, ns) => {
317
263
  let code = sourceFile.getFullText(), ctx = {
318
264
  bindings,
319
265
  computedArgRanges: [],
320
266
  hasReactiveImport: false,
321
- neededImports: new Set(),
267
+ ns,
322
268
  replacements: [],
323
269
  scopedBindings: [],
324
- sourceFile
270
+ sourceFile,
271
+ tmpCounter: 0
325
272
  };
326
273
  visit(ctx, sourceFile);
327
274
  if (ctx.replacements.length === 0) {
328
275
  return code;
329
276
  }
330
- let result = applyReplacements(code, ctx.replacements);
331
- if (ctx.neededImports.size > 0) {
332
- result = addMissingImports(result, ctx.neededImports);
333
- }
334
- return result;
277
+ return c.replace(code, ctx.replacements);
335
278
  };
336
- export { transformReactivePrimitives };
package/build/types.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { COMPUTED, SIGNAL, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
2
- import { ReactiveArray, ReactiveObject } from './reactive/index.js';
3
2
  import { ts } from '@esportsplus/typescript';
4
3
  type BindingType = 'array' | 'computed' | 'object' | 'signal';
5
4
  type Bindings = Map<string, BindingType>;
@@ -25,6 +24,7 @@ interface Link {
25
24
  sub: Computed<unknown>;
26
25
  version: number;
27
26
  }
27
+ type Reactive<T> = T;
28
28
  type Signal<T> = {
29
29
  subs: Link | null;
30
30
  subsTail: Link | null;
@@ -36,4 +36,4 @@ interface TransformResult {
36
36
  sourceFile: ts.SourceFile;
37
37
  transformed: boolean;
38
38
  }
39
- export type { BindingType, Bindings, Computed, Link, ReactiveArray, ReactiveObject, Signal, TransformResult };
39
+ export type { BindingType, Bindings, Computed, Link, Reactive, Signal, TransformResult };
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "@esportsplus/utilities": "^0.27.2"
5
5
  },
6
6
  "devDependencies": {
7
- "@esportsplus/typescript": "^0.17.4",
7
+ "@esportsplus/typescript": "^0.17.5",
8
8
  "@types/node": "^25.0.3",
9
9
  "vite": "^7.3.0"
10
10
  },
@@ -18,13 +18,13 @@
18
18
  "types": "./build/constants.d.ts"
19
19
  },
20
20
  "./plugins/tsc": {
21
+ "types": "./build/transformer/plugins/tsc.d.ts",
21
22
  "import": "./build/transformer/plugins/tsc.js",
22
- "require": "./build/transformer/plugins/tsc.js",
23
- "types": "./build/transformer/plugins/tsc.d.ts"
23
+ "require": "./build/transformer/plugins/tsc.js"
24
24
  },
25
25
  "./plugins/vite": {
26
- "import": "./build/transformer/plugins/vite.js",
27
- "types": "./build/transformer/plugins/vite.d.ts"
26
+ "types": "./build/transformer/plugins/vite.d.ts",
27
+ "import": "./build/transformer/plugins/vite.js"
28
28
  }
29
29
  },
30
30
  "main": "build/index.js",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "type": "module",
38
38
  "types": "build/index.d.ts",
39
- "version": "0.25.3",
39
+ "version": "0.25.5",
40
40
  "scripts": {
41
41
  "build": "tsc",
42
42
  "build:test": "pnpm build && vite build --config test/vite.config.ts",
package/readme.md CHANGED
@@ -107,7 +107,7 @@ The library requires a build-time transformer to convert `reactive()` calls into
107
107
  ```typescript
108
108
  // vite.config.ts
109
109
  import { defineConfig } from 'vite';
110
- import { plugin as reactivity } from '@esportsplus/reactivity/plugins/vite';
110
+ import reactivity from '@esportsplus/reactivity/plugins/vite';
111
111
 
112
112
  export default defineConfig({
113
113
  plugins: [
@@ -146,12 +146,12 @@ console.log(doubled);
146
146
 
147
147
  **Output:**
148
148
  ```typescript
149
- import { computed, read, set, signal } from '@esportsplus/reactivity';
149
+ import { computed, read, signal, write } from '@esportsplus/reactivity';
150
150
 
151
151
  let count = signal(0);
152
152
  let doubled = computed(() => read(count) * 2);
153
153
 
154
- set(count, 5);
154
+ write(count, 5);
155
155
  console.log(read(doubled));
156
156
  ```
157
157
 
@@ -172,7 +172,7 @@ class ReactiveObject_1 {
172
172
  #greeting = null;
173
173
 
174
174
  get name() { return read(this.#name); }
175
- set name(v) { set(this.#name, v); }
175
+ set name(v) { write(this.#name, v); }
176
176
  get greeting() { return read(this.#greeting ??= computed(() => `Hello, ${this.name}`)); }
177
177
 
178
178
  dispose() {
@@ -206,7 +206,7 @@ These are typically only used by the transformer output:
206
206
  | `signal(value)` | Creates a raw signal |
207
207
  | `computed(fn)` | Creates a raw computed |
208
208
  | `read(node)` | Reads a signal or computed value |
209
- | `set(signal, value)` | Sets a signal value |
209
+ | `write(signal, value)` | Sets a signal value |
210
210
  | `dispose(computed)` | Disposes a computed and its dependencies |
211
211
 
212
212
  ### Type Guards
package/src/constants.ts CHANGED
@@ -1,5 +1,22 @@
1
+ import uid from 'node_modules/@esportsplus/typescript/build/transformer/uid';
2
+
3
+ const COMPILATION_ENTRYPOINT = 'reactive';
4
+
5
+ const COMPILATION_ENTRYPOINT_REGEX = /\breactive\b/;
6
+
7
+ const COMPILATION_NAMESPACE = uid(COMPILATION_ENTRYPOINT);
8
+
9
+ const COMPILATION_TYPE_ARRAY = 'array';
10
+
11
+ const COMPILATION_TYPE_COMPUTED = 'computed';
12
+
13
+ const COMPILATION_TYPE_SIGNAL = 'signal';
14
+
15
+
1
16
  const COMPUTED = Symbol('reactivity.computed');
2
17
 
18
+ const PACKAGE = '@esportsplus/reactivity';
19
+
3
20
  const REACTIVE_ARRAY = Symbol('reactivity.reactive.array');
4
21
 
5
22
  const REACTIVE_OBJECT = Symbol('reactivity.reactive.object');
@@ -30,18 +47,10 @@ const STATE_NOTIFY_MASK = (STATE_CHECK | STATE_DIRTY);
30
47
 
31
48
 
32
49
  export {
33
- COMPUTED,
34
- REACTIVE_ARRAY,
35
- REACTIVE_OBJECT,
50
+ COMPILATION_TYPE_ARRAY, COMPILATION_TYPE_COMPUTED, COMPILATION_ENTRYPOINT, COMPILATION_ENTRYPOINT_REGEX, COMPILATION_NAMESPACE, COMPILATION_TYPE_SIGNAL, COMPUTED,
51
+ PACKAGE,
52
+ REACTIVE_ARRAY, REACTIVE_OBJECT,
36
53
  SIGNAL,
37
- STABILIZER_IDLE,
38
- STATE_NOTIFY_MASK,
39
- STABILIZER_RESCHEDULE,
40
- STABILIZER_RUNNING,
41
- STABILIZER_SCHEDULED,
42
- STATE_CHECK,
43
- STATE_DIRTY,
44
- STATE_IN_HEAP,
45
- STATE_NONE,
46
- STATE_RECOMPUTING
54
+ STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED,
55
+ STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING
47
56
  };
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED } from './constants';
2
- export { default as reactive } from './reactive';
1
+ export { REACTIVE_OBJECT } from './constants';
2
+ export { default as reactive, ReactiveArray } from './reactive';
3
3
  export * from './system';
4
4
  export * from './types';