@esportsplus/reactivity 0.29.0 → 0.29.1

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,5 @@
1
1
  import type { ReplacementIntent } from '@esportsplus/typescript/compiler';
2
2
  import { ts } from '@esportsplus/typescript';
3
3
  import type { Bindings } from '../types.js';
4
- declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings, checker?: ts.TypeChecker) => ReplacementIntent[];
4
+ declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings) => ReplacementIntent[];
5
5
  export default _default;
@@ -45,7 +45,7 @@ function visit(ctx, node) {
45
45
  ctx.bindings.set(node.name.text, COMPILER_TYPES.Array);
46
46
  }
47
47
  if (ts.isPropertyAccessExpression(node.initializer)) {
48
- let path = ast.getPropertyPathString(node.initializer);
48
+ let path = ast.getPropertyPath(node.initializer);
49
49
  if (path && ctx.bindings.get(path) === COMPILER_TYPES.Array) {
50
50
  ctx.bindings.set(node.name.text, COMPILER_TYPES.Array);
51
51
  }
@@ -92,10 +92,9 @@ function visit(ctx, node) {
92
92
  }
93
93
  ts.forEachChild(node, n => visit(ctx, n));
94
94
  }
95
- export default (sourceFile, bindings, checker) => {
95
+ export default (sourceFile, bindings) => {
96
96
  let ctx = {
97
97
  bindings,
98
- checker,
99
98
  replacements: [],
100
99
  sourceFile
101
100
  };
@@ -4,30 +4,10 @@ import { COMPILER_ENTRYPOINT, COMPILER_NAMESPACE, PACKAGE } from '../constants.j
4
4
  import array from './array.js';
5
5
  import object from './object.js';
6
6
  import primitives from './primitives.js';
7
- function hasReactiveImport(sourceFile) {
8
- return imports.find(sourceFile, PACKAGE).some(i => i.specifiers.has(COMPILER_ENTRYPOINT));
9
- }
10
- function isReactiveSymbol(checker, node) {
11
- let symbol = checker.getSymbolAtLocation(node);
12
- if (!symbol) {
13
- return false;
14
- }
15
- if (symbol.flags & ts.SymbolFlags.Alias) {
16
- symbol = checker.getAliasedSymbol(symbol);
17
- }
18
- let declarations = symbol.getDeclarations();
19
- if (!declarations || declarations.length === 0) {
20
- return false;
21
- }
22
- for (let i = 0, n = declarations.length; i < n; i++) {
23
- let decl = declarations[i], sourceFile = decl.getSourceFile();
24
- if (sourceFile.fileName.includes(PACKAGE) || sourceFile.fileName.includes('reactivity')) {
25
- if (symbol.name === COMPILER_ENTRYPOINT) {
26
- return true;
27
- }
28
- }
29
- }
30
- return false;
7
+ function findRemainingCalls(checker, sourceFile, transformedNodes) {
8
+ let ctx = { checker, replacements: [], sourceFile, transformedNodes };
9
+ visit(ctx, sourceFile);
10
+ return ctx.replacements;
31
11
  }
32
12
  function isReactiveCallExpression(checker, node) {
33
13
  if (!ts.isCallExpression(node)) {
@@ -39,39 +19,34 @@ function isReactiveCallExpression(checker, node) {
39
19
  return true;
40
20
  }
41
21
  if (checker) {
42
- return isReactiveSymbol(checker, expr);
22
+ return imports.inPackage(checker, expr, PACKAGE, COMPILER_ENTRYPOINT);
43
23
  }
44
24
  }
45
25
  if (ts.isPropertyAccessExpression(expr) && expr.name.text === COMPILER_ENTRYPOINT && checker) {
46
- return isReactiveSymbol(checker, expr);
26
+ return imports.inPackage(checker, expr, PACKAGE);
47
27
  }
48
28
  return false;
49
29
  }
30
+ function visit(ctx, node) {
31
+ if (isReactiveCallExpression(ctx.checker, node) && !ctx.transformedNodes.has(node) && !ctx.transformedNodes.has(node.expression)) {
32
+ ctx.replacements.push({
33
+ generate: () => `${COMPILER_NAMESPACE}.reactive(${node.arguments.map(a => a.getText(ctx.sourceFile)).join(', ')})`,
34
+ node
35
+ });
36
+ }
37
+ ts.forEachChild(node, n => visit(ctx, n));
38
+ }
50
39
  const plugin = {
51
40
  patterns: ['reactive(', 'reactive<'],
52
41
  transform: (ctx) => {
53
- if (!hasReactiveImport(ctx.sourceFile)) {
42
+ if (!imports.find(ctx.sourceFile, PACKAGE).some(i => i.specifiers.has(COMPILER_ENTRYPOINT))) {
54
43
  return {};
55
44
  }
56
- let bindings = new Map(), importsIntent = [], isReactiveCall = (node) => isReactiveCallExpression(ctx.checker, node), prepend = [], replacements = [];
57
- replacements.push(...primitives(ctx.sourceFile, bindings, isReactiveCall, ctx.checker));
58
- let objectResult = object(ctx.sourceFile, bindings, ctx.checker);
45
+ let bindings = new Map(), importsIntent = [], isReactive = (node) => isReactiveCallExpression(ctx.checker, node), prepend = [], replacements = [];
46
+ replacements.push(...primitives(ctx.sourceFile, bindings, isReactive));
47
+ let objectResult = object(ctx.sourceFile, bindings);
59
48
  prepend.push(...objectResult.prepend);
60
- replacements.push(...objectResult.replacements, ...array(ctx.sourceFile, bindings, ctx.checker));
61
- let transformedNodes = new Set(replacements.map(r => r.node));
62
- function findRemainingReactiveCalls(node) {
63
- if (isReactiveCall(node)) {
64
- let call = node;
65
- if (!transformedNodes.has(call) && !transformedNodes.has(call.expression)) {
66
- replacements.push({
67
- generate: () => `${COMPILER_NAMESPACE}.reactive(${call.arguments.map(a => a.getText(ctx.sourceFile)).join(', ')})`,
68
- node: call
69
- });
70
- }
71
- }
72
- ts.forEachChild(node, findRemainingReactiveCalls);
73
- }
74
- findRemainingReactiveCalls(ctx.sourceFile);
49
+ replacements.push(...objectResult.replacements, ...array(ctx.sourceFile, bindings), ...findRemainingCalls(ctx.checker, ctx.sourceFile, new Set(replacements.map(r => r.node))));
75
50
  if (replacements.length > 0 || prepend.length > 0) {
76
51
  importsIntent.push({
77
52
  namespace: COMPILER_NAMESPACE,
@@ -5,5 +5,5 @@ type ObjectTransformResult = {
5
5
  prepend: string[];
6
6
  replacements: ReplacementIntent[];
7
7
  };
8
- declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings, checker?: ts.TypeChecker) => ObjectTransformResult;
8
+ declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings) => ObjectTransformResult;
9
9
  export default _default;
@@ -145,11 +145,10 @@ function visit(ctx, node) {
145
145
  }
146
146
  ts.forEachChild(node, n => visit(ctx, n));
147
147
  }
148
- export default (sourceFile, bindings, checker) => {
148
+ export default (sourceFile, bindings) => {
149
149
  let ctx = {
150
150
  bindings,
151
151
  calls: [],
152
- checker,
153
152
  sourceFile
154
153
  };
155
154
  visit(ctx, sourceFile);
@@ -1,5 +1,5 @@
1
1
  import type { ReplacementIntent } from '@esportsplus/typescript/compiler';
2
2
  import { ts } from '@esportsplus/typescript';
3
3
  import type { Bindings } from '../types.js';
4
- declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings, isReactiveCall: (node: ts.Node) => boolean, checker?: ts.TypeChecker) => ReplacementIntent[];
4
+ declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings, isReactiveCall: (node: ts.Node) => boolean) => ReplacementIntent[];
5
5
  export default _default;
@@ -166,10 +166,9 @@ function visit(ctx, node) {
166
166
  }
167
167
  ts.forEachChild(node, n => visit(ctx, n));
168
168
  }
169
- export default (sourceFile, bindings, isReactiveCall, checker) => {
169
+ export default (sourceFile, bindings, isReactiveCall) => {
170
170
  let ctx = {
171
171
  bindings,
172
- checker,
173
172
  isReactiveCall,
174
173
  replacements: [],
175
174
  scopedBindings: [],
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "@esportsplus/utilities": "^0.27.2"
5
5
  },
6
6
  "devDependencies": {
7
- "@esportsplus/typescript": "^0.25.0",
7
+ "@esportsplus/typescript": "^0.26.0",
8
8
  "@types/node": "^25.0.3",
9
9
  "vite": "^7.3.1"
10
10
  },
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "type": "module",
33
33
  "types": "build/index.d.ts",
34
- "version": "0.29.0",
34
+ "version": "0.29.1",
35
35
  "scripts": {
36
36
  "build": "tsc",
37
37
  "build:test": "pnpm build && vite build --config test/vite.config.ts",
@@ -5,14 +5,6 @@ import { COMPILER_NAMESPACE, COMPILER_TYPES } from '~/constants';
5
5
  import type { Bindings } from '~/types';
6
6
 
7
7
 
8
- interface VisitContext {
9
- bindings: Bindings;
10
- checker?: ts.TypeChecker;
11
- replacements: ReplacementIntent[];
12
- sourceFile: ts.SourceFile;
13
- }
14
-
15
-
16
8
  function getElementTypeText(typeNode: ts.TypeNode, sourceFile: ts.SourceFile): string | null {
17
9
  if (ts.isArrayTypeNode(typeNode)) {
18
10
  return typeNode.elementType.getText(sourceFile);
@@ -31,7 +23,7 @@ function getElementTypeText(typeNode: ts.TypeNode, sourceFile: ts.SourceFile): s
31
23
  return null;
32
24
  }
33
25
 
34
- function visit(ctx: VisitContext, node: ts.Node): void {
26
+ function visit(ctx: { bindings: Bindings, replacements: ReplacementIntent[], sourceFile: ts.SourceFile }, node: ts.Node): void {
35
27
  if (
36
28
  ts.isCallExpression(node) &&
37
29
  ts.isIdentifier(node.expression) &&
@@ -72,7 +64,7 @@ function visit(ctx: VisitContext, node: ts.Node): void {
72
64
  }
73
65
 
74
66
  if (ts.isPropertyAccessExpression(node.initializer)) {
75
- let path = ast.getPropertyPathString(node.initializer);
67
+ let path = ast.getPropertyPath(node.initializer);
76
68
 
77
69
  if (path && ctx.bindings.get(path) === COMPILER_TYPES.Array) {
78
70
  ctx.bindings.set(node.name.text, COMPILER_TYPES.Array);
@@ -140,10 +132,9 @@ function visit(ctx: VisitContext, node: ts.Node): void {
140
132
  }
141
133
 
142
134
 
143
- export default (sourceFile: ts.SourceFile, bindings: Bindings, checker?: ts.TypeChecker): ReplacementIntent[] => {
144
- let ctx: VisitContext = {
135
+ export default (sourceFile: ts.SourceFile, bindings: Bindings): ReplacementIntent[] => {
136
+ let ctx = {
145
137
  bindings,
146
- checker,
147
138
  replacements: [],
148
139
  sourceFile
149
140
  };
@@ -8,42 +8,23 @@ import object from './object';
8
8
  import primitives from './primitives';
9
9
 
10
10
 
11
- function hasReactiveImport(sourceFile: ts.SourceFile): boolean {
12
- return imports.find(sourceFile, PACKAGE).some(i => i.specifiers.has(COMPILER_ENTRYPOINT));
13
- }
14
-
15
- function isReactiveSymbol(checker: ts.TypeChecker, node: ts.Node): boolean {
16
- let symbol = checker.getSymbolAtLocation(node);
17
-
18
- if (!symbol) {
19
- return false;
20
- }
21
-
22
- // Follow aliases to original symbol
23
- if (symbol.flags & ts.SymbolFlags.Alias) {
24
- symbol = checker.getAliasedSymbol(symbol);
25
- }
26
-
27
- let declarations = symbol.getDeclarations();
11
+ type FindRemainingContext = {
12
+ checker: ts.TypeChecker | undefined;
13
+ replacements: ReplacementIntent[];
14
+ sourceFile: ts.SourceFile;
15
+ transformedNodes: Set<ts.Node>;
16
+ };
28
17
 
29
- if (!declarations || declarations.length === 0) {
30
- return false;
31
- }
18
+ function findRemainingCalls(
19
+ checker: ts.TypeChecker | undefined,
20
+ sourceFile: ts.SourceFile,
21
+ transformedNodes: Set<ts.Node>
22
+ ): ReplacementIntent[] {
23
+ let ctx: FindRemainingContext = { checker, replacements: [], sourceFile, transformedNodes };
32
24
 
33
- for (let i = 0, n = declarations.length; i < n; i++) {
34
- let decl = declarations[i],
35
- sourceFile = decl.getSourceFile();
25
+ visit(ctx, sourceFile);
36
26
 
37
- // Check if declaration is from our package
38
- if (sourceFile.fileName.includes(PACKAGE) || sourceFile.fileName.includes('reactivity')) {
39
- // Verify it's the reactive export
40
- if (symbol.name === COMPILER_ENTRYPOINT) {
41
- return true;
42
- }
43
- }
44
- }
45
-
46
- return false;
27
+ return ctx.replacements;
47
28
  }
48
29
 
49
30
  function isReactiveCallExpression(checker: ts.TypeChecker | undefined, node: ts.Node): node is ts.CallExpression {
@@ -62,62 +43,58 @@ function isReactiveCallExpression(checker: ts.TypeChecker | undefined, node: ts.
62
43
 
63
44
  // Use checker to resolve aliases
64
45
  if (checker) {
65
- return isReactiveSymbol(checker, expr);
46
+ return imports.inPackage(checker, expr, PACKAGE, COMPILER_ENTRYPOINT);
66
47
  }
67
48
  }
68
49
 
69
50
  // Property access: ns.reactive(...)
70
51
  if (ts.isPropertyAccessExpression(expr) && expr.name.text === COMPILER_ENTRYPOINT && checker) {
71
- return isReactiveSymbol(checker, expr);
52
+ return imports.inPackage(checker, expr, PACKAGE);
72
53
  }
73
54
 
74
55
  return false;
75
56
  }
76
57
 
58
+ function visit(ctx: FindRemainingContext, node: ts.Node): void {
59
+ // Check if call or its expression has already been transformed
60
+ if (isReactiveCallExpression(ctx.checker, node) && !ctx.transformedNodes.has(node) && !ctx.transformedNodes.has(node.expression)) {
61
+ ctx.replacements.push({
62
+ generate: () => `${COMPILER_NAMESPACE}.reactive(${node.arguments.map(a => a.getText(ctx.sourceFile)).join(', ')})`,
63
+ node
64
+ });
65
+ }
66
+
67
+ ts.forEachChild(node, n => visit(ctx, n));
68
+ }
69
+
77
70
 
78
71
  const plugin: Plugin = {
79
72
  patterns: ['reactive(', 'reactive<'],
80
73
 
81
74
  transform: (ctx: TransformContext) => {
82
- if (!hasReactiveImport(ctx.sourceFile)) {
75
+ if (!imports.find(ctx.sourceFile, PACKAGE).some(i => i.specifiers.has(COMPILER_ENTRYPOINT))) {
83
76
  return {};
84
77
  }
85
78
 
86
79
  let bindings: Bindings = new Map(),
87
80
  importsIntent: ImportIntent[] = [],
88
- isReactiveCall = (node: ts.Node) => isReactiveCallExpression(ctx.checker, node),
81
+ isReactive = (node: ts.Node) => isReactiveCallExpression(ctx.checker, node),
89
82
  prepend: string[] = [],
90
83
  replacements: ReplacementIntent[] = [];
91
84
 
92
85
  // Run primitives transform first (tracks bindings for signal/computed)
93
- replacements.push(...primitives(ctx.sourceFile, bindings, isReactiveCall, ctx.checker));
86
+ replacements.push(...primitives(ctx.sourceFile, bindings, isReactive));
94
87
 
95
88
  // Run object transform
96
- let objectResult = object(ctx.sourceFile, bindings, ctx.checker);
89
+ let objectResult = object(ctx.sourceFile, bindings);
97
90
 
98
91
  prepend.push(...objectResult.prepend);
99
- replacements.push(...objectResult.replacements, ...array(ctx.sourceFile, bindings, ctx.checker));
100
-
101
- // Find remaining reactive() calls that weren't transformed and replace with namespace version
102
- let transformedNodes = new Set(replacements.map(r => r.node));
103
-
104
- function findRemainingReactiveCalls(node: ts.Node): void {
105
- if (isReactiveCall(node)) {
106
- let call = node as ts.CallExpression;
107
-
108
- // Check if call or its expression has already been transformed
109
- if (!transformedNodes.has(call) && !transformedNodes.has(call.expression)) {
110
- replacements.push({
111
- generate: () => `${COMPILER_NAMESPACE}.reactive(${call.arguments.map(a => a.getText(ctx.sourceFile)).join(', ')})`,
112
- node: call
113
- });
114
- }
115
- }
116
-
117
- ts.forEachChild(node, findRemainingReactiveCalls);
118
- }
119
-
120
- findRemainingReactiveCalls(ctx.sourceFile);
92
+ replacements.push(
93
+ ...objectResult.replacements,
94
+ ...array(ctx.sourceFile, bindings),
95
+ // Find remaining reactive() calls that weren't transformed and replace with namespace version
96
+ ...findRemainingCalls(ctx.checker, ctx.sourceFile, new Set(replacements.map(r => r.node)))
97
+ );
121
98
 
122
99
  // Build import intent
123
100
  if (replacements.length > 0 || prepend.length > 0) {
@@ -22,7 +22,6 @@ interface ReactiveObjectCall {
22
22
  interface VisitContext {
23
23
  bindings: Bindings;
24
24
  calls: ReactiveObjectCall[];
25
- checker?: ts.TypeChecker;
26
25
  sourceFile: ts.SourceFile;
27
26
  }
28
27
 
@@ -221,11 +220,10 @@ type ObjectTransformResult = {
221
220
  };
222
221
 
223
222
 
224
- export default (sourceFile: ts.SourceFile, bindings: Bindings, checker?: ts.TypeChecker): ObjectTransformResult => {
223
+ export default (sourceFile: ts.SourceFile, bindings: Bindings): ObjectTransformResult => {
225
224
  let ctx: VisitContext = {
226
225
  bindings,
227
226
  calls: [],
228
- checker,
229
227
  sourceFile
230
228
  };
231
229
 
@@ -245,9 +243,9 @@ export default (sourceFile: ts.SourceFile, bindings: Bindings, checker?: ts.Type
245
243
  replacements.push({
246
244
  generate: () => ` new ${call.className}(${
247
245
  call.properties
248
- .filter(({ isStatic, type }) => !isStatic || type === COMPILER_TYPES.Computed)
249
- .map(p => p.valueText)
250
- .join(', ')
246
+ .filter(({ isStatic, type }) => !isStatic || type === COMPILER_TYPES.Computed)
247
+ .map(p => p.valueText)
248
+ .join(', ')
251
249
  })`,
252
250
  node: call.node,
253
251
  });
@@ -12,7 +12,6 @@ interface ScopeBinding {
12
12
 
13
13
  interface TransformContext {
14
14
  bindings: Bindings;
15
- checker?: ts.TypeChecker;
16
15
  isReactiveCall: (node: ts.Node) => boolean;
17
16
  replacements: ReplacementIntent[];
18
17
  scopedBindings: ScopeBinding[];
@@ -239,15 +238,9 @@ function visit(ctx: TransformContext, node: ts.Node): void {
239
238
  }
240
239
 
241
240
 
242
- export default (
243
- sourceFile: ts.SourceFile,
244
- bindings: Bindings,
245
- isReactiveCall: (node: ts.Node) => boolean,
246
- checker?: ts.TypeChecker
247
- ) => {
241
+ export default (sourceFile: ts.SourceFile, bindings: Bindings, isReactiveCall: (node: ts.Node) => boolean) => {
248
242
  let ctx: TransformContext = {
249
243
  bindings,
250
- checker,
251
244
  isReactiveCall,
252
245
  replacements: [],
253
246
  scopedBindings: [],