@esportsplus/reactivity 0.25.1 → 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.
- package/build/reactive/index.d.ts +0 -2
- package/build/reactive/index.js +0 -1
- package/build/transformer/transforms/array.js +3 -0
- package/build/transformer/transforms/object.js +1 -1
- package/build/transformer/transforms/primitives.js +6 -5
- package/package.json +2 -2
- package/src/reactive/index.ts +1 -5
- package/src/transformer/transforms/array.ts +4 -0
- package/src/transformer/transforms/object.ts +1 -1
- package/src/transformer/transforms/primitives.ts +5 -4
- package/storage/tsc-transform-analysis-2026-01-04.md +125 -0
|
@@ -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 {
|
package/build/reactive/index.js
CHANGED
|
@@ -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 (
|
|
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.
|
|
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.
|
|
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",
|
package/src/reactive/index.ts
CHANGED
|
@@ -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?"
|