@esportsplus/reactivity 0.25.2 → 0.25.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,4 @@
1
1
  import { Prettify } from '@esportsplus/utilities';
2
- import { REACTIVE_OBJECT } from '../constants.js';
3
2
  import { ReactiveArray } from './array.js';
4
3
  declare const READONLY: unique symbol;
5
4
  type Infer<T> = T extends (...args: unknown[]) => Promise<infer R> ? R | undefined : T extends (...args: any[]) => infer R ? R : T extends (infer U)[] ? ReactiveArray<U> : T extends ReactiveObject<any> ? T : T extends Record<PropertyKey, unknown> ? {
@@ -8,7 +7,6 @@ type Infer<T> = T extends (...args: unknown[]) => Promise<infer R> ? R | undefin
8
7
  type ReactiveObject<T> = T extends Record<PropertyKey, unknown> ? Prettify<{
9
8
  [K in keyof T]: Infer<T[K]>;
10
9
  } & {
11
- [REACTIVE_OBJECT]: true;
12
10
  dispose: VoidFunction;
13
11
  }> : T extends (infer U)[] ? ReactiveArray<U> : never;
14
12
  type ReactiveObjectGuard<T> = T extends {
@@ -1,4 +1,3 @@
1
- import { REACTIVE_OBJECT } from '../constants.js';
2
1
  import { ReactiveArray } from './array.js';
3
2
  function reactive(_input) {
4
3
  throw new Error('@esportsplus/reactivity: reactive() called at runtime. ' +
@@ -23,6 +23,9 @@ function getPropertyPath(node) {
23
23
  }
24
24
  function isAssignmentTarget(node) {
25
25
  let parent = node.parent;
26
+ if (!parent) {
27
+ return false;
28
+ }
26
29
  if ((ts.isBinaryExpression(parent) && parent.left === node) ||
27
30
  ts.isPostfixUnaryExpression(parent) ||
28
31
  ts.isPrefixUnaryExpression(parent)) {
@@ -83,7 +83,7 @@ function visit(ctx, node) {
83
83
  let arg = node.arguments[0];
84
84
  if (arg && ts.isObjectLiteralExpression(arg)) {
85
85
  let varName = null;
86
- if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
86
+ if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
87
87
  varName = node.parent.name.text;
88
88
  ctx.bindings.set(varName, 'object');
89
89
  }
@@ -170,10 +170,11 @@ function visit(ctx, node) {
170
170
  let arg = node.arguments[0], classification = classifyReactiveArg(arg);
171
171
  if (classification) {
172
172
  let varName = null;
173
- if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
173
+ if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
174
174
  varName = node.parent.name.text;
175
175
  }
176
- else if (ts.isBinaryExpression(node.parent) &&
176
+ else if (node.parent &&
177
+ ts.isBinaryExpression(node.parent) &&
177
178
  node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
178
179
  ts.isIdentifier(node.parent.left)) {
179
180
  varName = node.parent.left.text;
@@ -215,7 +216,7 @@ function visit(ctx, node) {
215
216
  }
216
217
  }
217
218
  }
218
- if (ts.isIdentifier(node) && !isInDeclarationInit(node.parent)) {
219
+ if (ts.isIdentifier(node) && node.parent && !isInDeclarationInit(node.parent)) {
219
220
  if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
220
221
  ts.forEachChild(node, n => visit(ctx, n));
221
222
  return;
@@ -226,7 +227,7 @@ function visit(ctx, node) {
226
227
  return;
227
228
  }
228
229
  let binding = findBinding(ctx.scopedBindings, node.text, node), name = node.text;
229
- if (binding) {
230
+ if (binding && node.parent) {
230
231
  if (!isReactiveReassignment(node.parent) &&
231
232
  !(ts.isTypeOfExpression(node.parent) && node.parent.expression === node)) {
232
233
  let writeCtx = isWriteContext(node);
@@ -291,7 +292,7 @@ function visit(ctx, node) {
291
292
  ts.forEachChild(node, n => visit(ctx, n));
292
293
  }
293
294
  function visitArg(ctx, node) {
294
- if (ts.isIdentifier(node)) {
295
+ if (ts.isIdentifier(node) && node.parent) {
295
296
  if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
296
297
  ts.forEachChild(node, n => visitArg(ctx, n));
297
298
  return;
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.3",
7
+ "@esportsplus/typescript": "^0.17.4",
8
8
  "@types/node": "^25.0.3",
9
9
  "vite": "^7.3.0"
10
10
  },
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "type": "module",
38
38
  "types": "build/index.d.ts",
39
- "version": "0.25.2",
39
+ "version": "0.25.3",
40
40
  "scripts": {
41
41
  "build": "tsc",
42
42
  "build:test": "pnpm build && vite build --config test/vite.config.ts",
@@ -1,5 +1,4 @@
1
1
  import { Prettify } from '@esportsplus/utilities';
2
- import { REACTIVE_OBJECT } from '~/constants';
3
2
  import { ReactiveArray } from './array';
4
3
 
5
4
 
@@ -21,10 +20,7 @@ type Infer<T> =
21
20
 
22
21
  type ReactiveObject<T> =
23
22
  T extends Record<PropertyKey, unknown>
24
- ? Prettify<{ [K in keyof T]: Infer<T[K]> } & {
25
- [REACTIVE_OBJECT]: true;
26
- dispose: VoidFunction
27
- }>
23
+ ? Prettify<{ [K in keyof T]: Infer<T[K]> } & { dispose: VoidFunction }>
28
24
  : T extends (infer U)[]
29
25
  ? ReactiveArray<U>
30
26
  : never;
@@ -42,6 +42,10 @@ function getPropertyPath(node: ts.PropertyAccessExpression): string | null {
42
42
  function isAssignmentTarget(node: ts.Node): boolean {
43
43
  let parent = node.parent;
44
44
 
45
+ if (!parent) {
46
+ return false;
47
+ }
48
+
45
49
  if (
46
50
  (ts.isBinaryExpression(parent) && parent.left === node) ||
47
51
  ts.isPostfixUnaryExpression(parent) ||
@@ -140,7 +140,7 @@ function visit(ctx: TransformContext, node: ts.Node): void {
140
140
  if (arg && ts.isObjectLiteralExpression(arg)) {
141
141
  let varName: string | null = null;
142
142
 
143
- if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
143
+ if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
144
144
  varName = node.parent.name.text;
145
145
  ctx.bindings.set(varName, 'object');
146
146
  }
@@ -245,10 +245,11 @@ function visit(ctx: TransformContext, node: ts.Node): void {
245
245
  if (classification) {
246
246
  let varName: string | null = null;
247
247
 
248
- if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
248
+ if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
249
249
  varName = node.parent.name.text;
250
250
  }
251
251
  else if (
252
+ node.parent &&
252
253
  ts.isBinaryExpression(node.parent) &&
253
254
  node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
254
255
  ts.isIdentifier(node.parent.left)
@@ -303,7 +304,7 @@ function visit(ctx: TransformContext, node: ts.Node): void {
303
304
  }
304
305
  }
305
306
 
306
- if (ts.isIdentifier(node) && !isInDeclarationInit(node.parent)) {
307
+ if (ts.isIdentifier(node) && node.parent && !isInDeclarationInit(node.parent)) {
307
308
  if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
308
309
  ts.forEachChild(node, n => visit(ctx, n));
309
310
  return;
@@ -319,7 +320,7 @@ function visit(ctx: TransformContext, node: ts.Node): void {
319
320
  let binding = findBinding(ctx.scopedBindings, node.text, node),
320
321
  name = node.text;
321
322
 
322
- if (binding) {
323
+ if (binding && node.parent) {
323
324
  if (
324
325
  !isReactiveReassignment(node.parent) &&
325
326
  !(ts.isTypeOfExpression(node.parent) && node.parent.expression === node)
@@ -399,7 +400,7 @@ function visit(ctx: TransformContext, node: ts.Node): void {
399
400
  }
400
401
 
401
402
  function visitArg(ctx: ArgContext, node: ts.Node): void {
402
- if (ts.isIdentifier(node)) {
403
+ if (ts.isIdentifier(node) && node.parent) {
403
404
  if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
404
405
  ts.forEachChild(node, n => visitArg(ctx, n));
405
406
  return;
@@ -0,0 +1,125 @@
1
+ # TSC Transform Analysis Report: @esportsplus/reactivity
2
+
3
+ ## Executive Summary
4
+
5
+ 1. **The library build is working as designed** - the reactivity library itself doesn't need transformation
6
+ 2. **Test build transformations work correctly** - vite plugin transforms `reactive()` calls
7
+ 3. **Confusion point**: The library PROVIDES transformers, it doesn't CONSUME them
8
+
9
+ ## Findings
10
+
11
+ ### How @esportsplus/typescript Custom TSC Works
12
+
13
+ | File | Purpose |
14
+ |------|---------|
15
+ | [bin/tsc](node_modules/.pnpm/@esportsplus+typescript@0.17.3/node_modules/@esportsplus/typescript/bin/tsc) | Entry point - calls build/cli/tsc.js |
16
+ | [build/cli/tsc.js:122-137](node_modules/.pnpm/@esportsplus+typescript@0.17.3/node_modules/@esportsplus/typescript/build/cli/tsc.js#L122-L137) | Main logic - checks for plugins in tsconfig |
17
+
18
+ **Critical Logic** (cli/tsc.js:128-131):
19
+ ```javascript
20
+ let plugins = getPlugins(tsconfig);
21
+ if (plugins.length === 0) {
22
+ passthrough(); // Falls back to standard tsc
23
+ return;
24
+ }
25
+ ```
26
+
27
+ **Plugin Detection** (cli/tsc.js:97-98):
28
+ ```javascript
29
+ return config?.compilerOptions?.plugins?.filter(
30
+ (p) => typeof p === 'object' && p !== null && 'transform' in p
31
+ ) ?? [];
32
+ ```
33
+
34
+ ### Why Library Build Has No Transformations
35
+
36
+ | Issue | Evidence |
37
+ |-------|----------|
38
+ | No plugins configured | [tsconfig.json:1-3](tsconfig.json#L1-L3) only extends base config, no `compilerOptions.plugins` |
39
+ | Library defines `reactive()` | [src/reactive/index.ts:42-47](src/reactive/index.ts#L42-L47) - throws at runtime by design |
40
+ | Library doesn't use `reactive()` | Source files don't call `reactive()` as a consumer would |
41
+
42
+ ### Test Build Works Correctly
43
+
44
+ **Source** ([test/primitives.ts:11](test/primitives.ts#L11)):
45
+ ```typescript
46
+ let count = reactive(0);
47
+ count = 10;
48
+ console.log('Initial count:', count);
49
+ ```
50
+
51
+ **Transformed Output** ([test/build/primitives.js:48-51](test/build/primitives.js#L48-L51)):
52
+ ```javascript
53
+ let count = signal(0);
54
+ set(count, 10);
55
+ console.log("Initial count:", read(count));
56
+ ```
57
+
58
+ **Reactive Objects** transform to classes with signal-backed getters/setters:
59
+ ```javascript
60
+ class ReactiveObject_xxx {
61
+ #count = signal(0);
62
+ get count() { return read(this.#count); }
63
+ set count(v) { set(this.#count, v); }
64
+ }
65
+ ```
66
+
67
+ ## Architecture
68
+
69
+ ```
70
+ @esportsplus/reactivity
71
+ ├── src/ # Library source (NOT transformed)
72
+ │ ├── reactive/ # reactive() function definition
73
+ │ └── transformer/ # Transformer implementation
74
+ │ └── plugins/
75
+ │ ├── tsc.ts # TSC plugin for consumers
76
+ │ └── vite.ts # Vite plugin for consumers
77
+ ├── build/ # Compiled library (standard tsc output)
78
+ └── test/ # Test files USING reactive()
79
+ └── build/ # Transformed test output (via vite)
80
+ ```
81
+
82
+ ## Build Commands
83
+
84
+ | Command | What It Does | Uses Transformer? |
85
+ |---------|--------------|-------------------|
86
+ | `pnpm build` | Compiles library src/ to build/ | No - intentional |
87
+ | `pnpm build:test` | Compiles test/ via vite | Yes - via vite plugin |
88
+
89
+ ## Expected Behavior
90
+
91
+ The reactivity library follows a **compile-time transformation** pattern:
92
+
93
+ 1. **Library provides**: `reactive()` function + transformer plugins
94
+ 2. **Consumers configure**: transformer plugin in their build (vite/tsc)
95
+ 3. **At build time**: `reactive()` calls → signal/read/set/computed calls
96
+ 4. **At runtime**: If transformation didn't happen, `reactive()` throws with helpful error
97
+
98
+ ## If You Want TSC Plugins in Library Build
99
+
100
+ Add to [tsconfig.json](tsconfig.json):
101
+ ```json
102
+ {
103
+ "extends": "@esportsplus/typescript/tsconfig.package.json",
104
+ "compilerOptions": {
105
+ "plugins": [
106
+ { "transform": "@esportsplus/reactivity/plugins/tsc" }
107
+ ]
108
+ }
109
+ }
110
+ ```
111
+
112
+ **Note**: This would only affect files that actually use `reactive()` - the library source doesn't, so no change would occur.
113
+
114
+ ## Recommended Actions
115
+
116
+ 1. **If transformations ARE expected in library build**: Add plugins config to tsconfig.json
117
+ 2. **If testing transformer**: Run `pnpm build:test` to see transformations
118
+ 3. **If issue is in consuming project**: Ensure plugin is configured in consumer's build
119
+
120
+ ## Next Steps
121
+
122
+ Clarify the specific scenario where transformations aren't working:
123
+ - "Show me the file that should be transformed"
124
+ - "What build command are you running?"
125
+ - "Is this the library or a consuming project?"