@esportsplus/reactivity 0.26.1 → 0.27.0

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,4 +1,4 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
2
  import type { Bindings } from '../types.js';
3
- declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings, _ns: string) => string;
3
+ declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings, _ns: string, _checker?: ts.TypeChecker) => string;
4
4
  export default _default;
@@ -54,7 +54,7 @@ function visit(ctx, node) {
54
54
  }
55
55
  ts.forEachChild(node, n => visit(ctx, n));
56
56
  }
57
- export default (sourceFile, bindings, _ns) => {
57
+ export default (sourceFile, bindings, _ns, _checker) => {
58
58
  let code = sourceFile.getFullText(), ctx = {
59
59
  bindings,
60
60
  replacements: [],
@@ -1,4 +1,4 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
2
  import type { TransformResult } from '../types.js';
3
- declare const transform: (sourceFile: ts.SourceFile) => TransformResult;
3
+ declare const transform: (sourceFile: ts.SourceFile, program: ts.Program) => TransformResult;
4
4
  export { transform };
@@ -1,51 +1,46 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
- import { code as c } from '@esportsplus/typescript/compiler';
3
- import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REGEX, COMPILER_NAMESPACE } from '../constants.js';
2
+ import { code as c, imports } from '@esportsplus/typescript/compiler';
3
+ import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REGEX, COMPILER_NAMESPACE, PACKAGE } from '../constants.js';
4
4
  import array from './array.js';
5
5
  import object from './object.js';
6
6
  import primitives from './primitives.js';
7
7
  let transforms = [object, array, primitives];
8
- function contains(code) {
9
- if (!c.contains(code, { regex: COMPILER_ENTRYPOINT_REGEX })) {
10
- return false;
8
+ function hasReactiveImport(sourceFile) {
9
+ let found = imports.find(sourceFile, PACKAGE);
10
+ for (let i = 0, n = found.length; i < n; i++) {
11
+ if (found[i].specifiers.has(COMPILER_ENTRYPOINT)) {
12
+ return true;
13
+ }
11
14
  }
12
- let ctx = {
13
- imported: false,
14
- used: false
15
- };
16
- visit(ctx, ts.createSourceFile('detect.ts', code, ts.ScriptTarget.Latest, false));
17
- return ctx.imported && ctx.used;
15
+ return false;
18
16
  }
19
- function visit(ctx, node) {
20
- if (ctx.imported && ctx.used) {
21
- return;
17
+ function hasReactiveUsage(code) {
18
+ if (!c.contains(code, { regex: COMPILER_ENTRYPOINT_REGEX })) {
19
+ return false;
22
20
  }
23
- if (ts.isImportDeclaration(node) &&
24
- node.importClause?.namedBindings &&
25
- ts.isNamedImports(node.importClause.namedBindings)) {
26
- let elements = node.importClause.namedBindings.elements;
27
- for (let i = 0, n = elements.length; i < n; i++) {
28
- let element = elements[i];
29
- if ((element.propertyName?.text ?? element.name.text) === COMPILER_ENTRYPOINT) {
30
- ctx.imported = true;
31
- break;
32
- }
21
+ let sourceFile = ts.createSourceFile('detect.ts', code, ts.ScriptTarget.Latest, false), used = false;
22
+ function visit(node) {
23
+ if (used) {
24
+ return;
33
25
  }
26
+ if (ts.isCallExpression(node) &&
27
+ ts.isIdentifier(node.expression) &&
28
+ node.expression.text === COMPILER_ENTRYPOINT) {
29
+ used = true;
30
+ return;
31
+ }
32
+ ts.forEachChild(node, visit);
34
33
  }
35
- if (ts.isCallExpression(node) &&
36
- ts.isIdentifier(node.expression) &&
37
- node.expression.text === COMPILER_ENTRYPOINT) {
38
- ctx.used = true;
39
- }
40
- ts.forEachChild(node, n => visit(ctx, n));
34
+ visit(sourceFile);
35
+ return used;
41
36
  }
42
- const transform = (sourceFile) => {
43
- let bindings = new Map(), changed = false, code = sourceFile.getFullText(), current = sourceFile, result;
44
- if (!contains(code)) {
37
+ const transform = (sourceFile, program) => {
38
+ let bindings = new Map(), changed = false, checker = program.getTypeChecker(), code = sourceFile.getFullText(), current = sourceFile, result;
39
+ if (!hasReactiveImport(sourceFile) || !hasReactiveUsage(code)) {
45
40
  return { changed: false, code, sourceFile };
46
41
  }
47
42
  for (let i = 0, n = transforms.length; i < n; i++) {
48
- result = transforms[i](current, bindings, COMPILER_NAMESPACE);
43
+ result = transforms[i](current, bindings, COMPILER_NAMESPACE, checker);
49
44
  if (result !== code) {
50
45
  current = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
51
46
  code = result;
@@ -53,6 +48,7 @@ const transform = (sourceFile) => {
53
48
  }
54
49
  }
55
50
  if (changed) {
51
+ code = imports.modify(code, current, PACKAGE, { remove: [COMPILER_ENTRYPOINT] });
56
52
  code = `import * as ${COMPILER_NAMESPACE} from '@esportsplus/reactivity';\n` + code;
57
53
  sourceFile = ts.createSourceFile(sourceFile.fileName, code, sourceFile.languageVersion, true);
58
54
  }
@@ -1,4 +1,4 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
2
  import type { Bindings } from '../types.js';
3
- declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings, ns: string) => string;
3
+ declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings, ns: string, checker?: ts.TypeChecker) => string;
4
4
  export default _default;
@@ -1,6 +1,6 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
- import { code as c } from '@esportsplus/typescript/compiler';
3
- import { COMPILER_TYPES, PACKAGE } from '../constants.js';
2
+ import { code as c, imports } from '@esportsplus/typescript/compiler';
3
+ import { COMPILER_ENTRYPOINT, COMPILER_TYPES, PACKAGE } from '../constants.js';
4
4
  function analyzeProperty(prop, sourceFile) {
5
5
  if (!ts.isPropertyAssignment(prop)) {
6
6
  return null;
@@ -60,27 +60,21 @@ function buildClassCode(className, properties, ns) {
60
60
  }
61
61
  `;
62
62
  }
63
+ function isReactiveCall(node, checker) {
64
+ if (!ts.isIdentifier(node.expression)) {
65
+ return false;
66
+ }
67
+ if (node.expression.text !== COMPILER_ENTRYPOINT) {
68
+ return false;
69
+ }
70
+ return imports.isFromPackage(node.expression, PACKAGE, checker);
71
+ }
63
72
  function visit(ctx, node) {
64
73
  if (ts.isImportDeclaration(node)) {
65
74
  ctx.lastImportEnd = node.end;
66
- if (ts.isStringLiteral(node.moduleSpecifier) &&
67
- node.moduleSpecifier.text.includes(PACKAGE)) {
68
- let clause = node.importClause;
69
- if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
70
- let elements = clause.namedBindings.elements;
71
- for (let i = 0, n = elements.length; i < n; i++) {
72
- if (elements[i].name.text === 'reactive') {
73
- ctx.hasReactiveImport = true;
74
- break;
75
- }
76
- }
77
- }
78
- }
79
75
  }
80
- if (ctx.hasReactiveImport &&
81
- ts.isCallExpression(node) &&
82
- ts.isIdentifier(node.expression) &&
83
- node.expression.text === 'reactive') {
76
+ if (ts.isCallExpression(node) &&
77
+ isReactiveCall(node, ctx.checker)) {
84
78
  let arg = node.arguments[0];
85
79
  if (arg && ts.isObjectLiteralExpression(arg)) {
86
80
  let properties = [], props = arg.properties, varName = null;
@@ -115,12 +109,12 @@ function visit(ctx, node) {
115
109
  }
116
110
  ts.forEachChild(node, n => visit(ctx, n));
117
111
  }
118
- export default (sourceFile, bindings, ns) => {
112
+ export default (sourceFile, bindings, ns, checker) => {
119
113
  let code = sourceFile.getFullText(), ctx = {
120
114
  bindings,
121
115
  calls: [],
116
+ checker,
122
117
  classCounter: 0,
123
- hasReactiveImport: false,
124
118
  lastImportEnd: 0,
125
119
  ns,
126
120
  sourceFile
@@ -1,4 +1,4 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
2
  import type { Bindings } from '../types.js';
3
- declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings, ns: string) => string;
3
+ declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings, ns: string, checker?: ts.TypeChecker) => string;
4
4
  export default _default;
@@ -1,5 +1,5 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
- import { ast, code as c } from '@esportsplus/typescript/compiler';
2
+ import { ast, code as c, imports } from '@esportsplus/typescript/compiler';
3
3
  import { COMPILER_ENTRYPOINT, COMPILER_TYPES, PACKAGE } from '../constants.js';
4
4
  const COMPOUND_OPERATORS = new Map([
5
5
  [ts.SyntaxKind.AmpersandAmpersandEqualsToken, '&&'],
@@ -58,15 +58,22 @@ function isInScope(reference, binding) {
58
58
  }
59
59
  return false;
60
60
  }
61
- function isReactiveReassignment(node) {
61
+ function isReactiveCall(node, checker) {
62
+ if (!ts.isIdentifier(node.expression)) {
63
+ return false;
64
+ }
65
+ if (node.expression.text !== COMPILER_ENTRYPOINT) {
66
+ return false;
67
+ }
68
+ return imports.isFromPackage(node.expression, PACKAGE, checker);
69
+ }
70
+ function isReactiveReassignment(node, checker) {
62
71
  let parent = node.parent;
63
72
  if (ts.isBinaryExpression(parent) &&
64
73
  parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
65
74
  parent.right === node &&
66
- ts.isCallExpression(node) &&
67
- ts.isIdentifier(node.expression) &&
68
- node.expression.text === COMPILER_ENTRYPOINT) {
69
- return true;
75
+ ts.isCallExpression(node)) {
76
+ return isReactiveCall(node, checker);
70
77
  }
71
78
  return false;
72
79
  }
@@ -90,24 +97,9 @@ function isWriteContext(node) {
90
97
  return false;
91
98
  }
92
99
  function visit(ctx, node) {
93
- if (ts.isImportDeclaration(node) &&
94
- ts.isStringLiteral(node.moduleSpecifier) &&
95
- node.moduleSpecifier.text.includes(PACKAGE)) {
96
- let clause = node.importClause;
97
- if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
98
- for (let i = 0, n = clause.namedBindings.elements.length; i < n; i++) {
99
- if (clause.namedBindings.elements[i].name.text === COMPILER_ENTRYPOINT) {
100
- ctx.hasReactiveImport = true;
101
- break;
102
- }
103
- }
104
- }
105
- }
106
- if (ctx.hasReactiveImport &&
107
- ts.isCallExpression(node) &&
108
- ts.isIdentifier(node.expression) &&
109
- node.expression.text === COMPILER_ENTRYPOINT &&
110
- node.arguments.length > 0) {
100
+ if (ts.isCallExpression(node) &&
101
+ node.arguments.length > 0 &&
102
+ isReactiveCall(node, ctx.checker)) {
111
103
  let arg = node.arguments[0], classification = COMPILER_TYPES.Signal;
112
104
  if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
113
105
  classification = COMPILER_TYPES.Computed;
@@ -170,7 +162,7 @@ function visit(ctx, node) {
170
162
  }
171
163
  let binding = findBinding(ctx.scopedBindings, node.text, node), name = node.text;
172
164
  if (binding && node.parent) {
173
- if (!isReactiveReassignment(node.parent) &&
165
+ if (!isReactiveReassignment(node.parent, ctx.checker) &&
174
166
  !(ts.isTypeOfExpression(node.parent) && node.parent.expression === node)) {
175
167
  let writeCtx = isWriteContext(node);
176
168
  if (writeCtx) {
@@ -247,11 +239,11 @@ function visitArg(ctx, node) {
247
239
  }
248
240
  ts.forEachChild(node, n => visitArg(ctx, n));
249
241
  }
250
- export default (sourceFile, bindings, ns) => {
242
+ export default (sourceFile, bindings, ns, checker) => {
251
243
  let code = sourceFile.getFullText(), ctx = {
252
244
  bindings,
245
+ checker,
253
246
  computedArgRanges: [],
254
- hasReactiveImport: false,
255
247
  ns,
256
248
  replacements: [],
257
249
  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.21.0",
7
+ "@esportsplus/typescript": "^0.22.0",
8
8
  "@types/node": "^25.0.3",
9
9
  "vite": "^7.3.0"
10
10
  },
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "type": "module",
33
33
  "types": "build/index.d.ts",
34
- "version": "0.26.1",
34
+ "version": "0.27.0",
35
35
  "scripts": {
36
36
  "build": "tsc",
37
37
  "build:test": "pnpm build && vite build --config test/vite.config.ts",
@@ -81,7 +81,7 @@ function visit(ctx: { bindings: Bindings, replacements: Replacement[], sourceFil
81
81
  }
82
82
 
83
83
 
84
- export default (sourceFile: ts.SourceFile, bindings: Bindings, _ns: string): string => {
84
+ export default (sourceFile: ts.SourceFile, bindings: Bindings, _ns: string, _checker?: ts.TypeChecker): string => {
85
85
  let code = sourceFile.getFullText(),
86
86
  ctx = {
87
87
  bindings,
@@ -1,6 +1,6 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
- import { code as c } from '@esportsplus/typescript/compiler';
3
- import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REGEX, COMPILER_NAMESPACE } from '~/constants';
2
+ import { code as c, imports } from '@esportsplus/typescript/compiler';
3
+ import { COMPILER_ENTRYPOINT, COMPILER_ENTRYPOINT_REGEX, COMPILER_NAMESPACE, PACKAGE } from '~/constants';
4
4
  import type { Bindings, TransformResult } from '~/types';
5
5
  import array from './array';
6
6
  import object from './object';
@@ -10,68 +10,63 @@ import primitives from './primitives';
10
10
  let transforms = [object, array, primitives];
11
11
 
12
12
 
13
- function contains(code: string): boolean {
14
- if (!c.contains(code, { regex: COMPILER_ENTRYPOINT_REGEX })) {
15
- return false;
16
- }
13
+ function hasReactiveImport(sourceFile: ts.SourceFile): boolean {
14
+ let found = imports.find(sourceFile, PACKAGE);
17
15
 
18
- let ctx = {
19
- imported: false,
20
- used: false
21
- };
22
-
23
- visit(ctx, ts.createSourceFile('detect.ts', code, ts.ScriptTarget.Latest, false));
16
+ for (let i = 0, n = found.length; i < n; i++) {
17
+ if (found[i].specifiers.has(COMPILER_ENTRYPOINT)) {
18
+ return true;
19
+ }
20
+ }
24
21
 
25
- return ctx.imported && ctx.used;
22
+ return false;
26
23
  }
27
24
 
28
- function visit(ctx: { imported: boolean; used: boolean; }, node: ts.Node): void {
29
- if (ctx.imported && ctx.used) {
30
- return;
25
+ function hasReactiveUsage(code: string): boolean {
26
+ if (!c.contains(code, { regex: COMPILER_ENTRYPOINT_REGEX })) {
27
+ return false;
31
28
  }
32
29
 
33
- if (
34
- ts.isImportDeclaration(node) &&
35
- node.importClause?.namedBindings &&
36
- ts.isNamedImports(node.importClause.namedBindings)
37
- ) {
38
- let elements = node.importClause.namedBindings.elements;
30
+ let sourceFile = ts.createSourceFile('detect.ts', code, ts.ScriptTarget.Latest, false),
31
+ used = false;
39
32
 
40
- for (let i = 0, n = elements.length; i < n; i++) {
41
- let element = elements[i];
33
+ function visit(node: ts.Node): void {
34
+ if (used) {
35
+ return;
36
+ }
42
37
 
43
- if ((element.propertyName?.text ?? element.name.text) === COMPILER_ENTRYPOINT) {
44
- ctx.imported = true;
45
- break;
46
- }
38
+ if (
39
+ ts.isCallExpression(node) &&
40
+ ts.isIdentifier(node.expression) &&
41
+ node.expression.text === COMPILER_ENTRYPOINT
42
+ ) {
43
+ used = true;
44
+ return;
47
45
  }
48
- }
49
46
 
50
- if (
51
- ts.isCallExpression(node) &&
52
- ts.isIdentifier(node.expression) &&
53
- node.expression.text === COMPILER_ENTRYPOINT
54
- ) {
55
- ctx.used = true;
47
+ ts.forEachChild(node, visit);
56
48
  }
57
49
 
58
- ts.forEachChild(node, n => visit(ctx, n));
50
+ visit(sourceFile);
51
+
52
+ return used;
59
53
  }
60
54
 
61
55
 
62
- const transform = (sourceFile: ts.SourceFile): TransformResult => {
56
+ const transform = (sourceFile: ts.SourceFile, program: ts.Program): TransformResult => {
63
57
  let bindings: Bindings = new Map(),
64
58
  changed = false,
59
+ checker = program.getTypeChecker(),
65
60
  code = sourceFile.getFullText(),
66
61
  current = sourceFile,
67
62
  result: string;
68
63
 
69
- if (!contains(code)) {
64
+ if (!hasReactiveImport(sourceFile) || !hasReactiveUsage(code)) {
70
65
  return { changed: false, code, sourceFile };
71
66
  }
72
67
 
73
68
  for (let i = 0, n = transforms.length; i < n; i++) {
74
- result = transforms[i](current, bindings, COMPILER_NAMESPACE);
69
+ result = transforms[i](current, bindings, COMPILER_NAMESPACE, checker);
75
70
 
76
71
  if (result !== code) {
77
72
  current = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
@@ -81,6 +76,7 @@ const transform = (sourceFile: ts.SourceFile): TransformResult => {
81
76
  }
82
77
 
83
78
  if (changed) {
79
+ code = imports.modify(code, current, PACKAGE, { remove: [COMPILER_ENTRYPOINT] });
84
80
  code = `import * as ${COMPILER_NAMESPACE} from '@esportsplus/reactivity';\n` + code;
85
81
  sourceFile = ts.createSourceFile(sourceFile.fileName, code, sourceFile.languageVersion, true);
86
82
  }
@@ -1,6 +1,6 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
- import { code as c, type Replacement } from '@esportsplus/typescript/compiler';
3
- import { COMPILER_TYPES, PACKAGE } from '~/constants';
2
+ import { code as c, imports, type Replacement } from '@esportsplus/typescript/compiler';
3
+ import { COMPILER_ENTRYPOINT, COMPILER_TYPES, PACKAGE } from '~/constants';
4
4
  import type { Bindings } from '~/types';
5
5
 
6
6
 
@@ -21,8 +21,8 @@ interface ReactiveObjectCall {
21
21
  interface TransformContext {
22
22
  bindings: Bindings;
23
23
  calls: ReactiveObjectCall[];
24
+ checker?: ts.TypeChecker;
24
25
  classCounter: number;
25
- hasReactiveImport: boolean;
26
26
  lastImportEnd: number;
27
27
  ns: string;
28
28
  sourceFile: ts.SourceFile;
@@ -109,34 +109,26 @@ function buildClassCode(className: string, properties: AnalyzedProperty[], ns: s
109
109
  `;
110
110
  }
111
111
 
112
- function visit(ctx: TransformContext, node: ts.Node): void {
113
- if (ts.isImportDeclaration(node)) {
114
- ctx.lastImportEnd = node.end;
112
+ function isReactiveCall(node: ts.CallExpression, checker?: ts.TypeChecker): boolean {
113
+ if (!ts.isIdentifier(node.expression)) {
114
+ return false;
115
+ }
115
116
 
116
- if (
117
- ts.isStringLiteral(node.moduleSpecifier) &&
118
- node.moduleSpecifier.text.includes(PACKAGE)
119
- ) {
120
- let clause = node.importClause;
117
+ if (node.expression.text !== COMPILER_ENTRYPOINT) {
118
+ return false;
119
+ }
121
120
 
122
- if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
123
- let elements = clause.namedBindings.elements;
121
+ return imports.isFromPackage(node.expression, PACKAGE, checker);
122
+ }
124
123
 
125
- for (let i = 0, n = elements.length; i < n; i++) {
126
- if (elements[i].name.text === 'reactive') {
127
- ctx.hasReactiveImport = true;
128
- break;
129
- }
130
- }
131
- }
132
- }
124
+ function visit(ctx: TransformContext, node: ts.Node): void {
125
+ if (ts.isImportDeclaration(node)) {
126
+ ctx.lastImportEnd = node.end;
133
127
  }
134
128
 
135
129
  if (
136
- ctx.hasReactiveImport &&
137
130
  ts.isCallExpression(node) &&
138
- ts.isIdentifier(node.expression) &&
139
- node.expression.text === 'reactive'
131
+ isReactiveCall(node, ctx.checker)
140
132
  ) {
141
133
  let arg = node.arguments[0];
142
134
 
@@ -186,13 +178,13 @@ function visit(ctx: TransformContext, node: ts.Node): void {
186
178
  }
187
179
 
188
180
 
189
- export default (sourceFile: ts.SourceFile, bindings: Bindings, ns: string): string => {
181
+ export default (sourceFile: ts.SourceFile, bindings: Bindings, ns: string, checker?: ts.TypeChecker): string => {
190
182
  let code = sourceFile.getFullText(),
191
183
  ctx: TransformContext = {
192
184
  bindings,
193
185
  calls: [],
186
+ checker,
194
187
  classCounter: 0,
195
- hasReactiveImport: false,
196
188
  lastImportEnd: 0,
197
189
  ns,
198
190
  sourceFile
@@ -224,4 +216,5 @@ export default (sourceFile: ts.SourceFile, bindings: Bindings, ns: string): stri
224
216
  }
225
217
 
226
218
  return c.replace(code, replacements);
227
- };
219
+ };
220
+
@@ -1,7 +1,7 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
- import { ast, code as c, type Range, type Replacement } from '@esportsplus/typescript/compiler';
3
- import type { Bindings } from '~/types';
2
+ import { ast, code as c, imports, type Range, type Replacement } from '@esportsplus/typescript/compiler';
4
3
  import { COMPILER_ENTRYPOINT, COMPILER_TYPES, PACKAGE } from '~/constants';
4
+ import type { Bindings } from '~/types';
5
5
 
6
6
 
7
7
  interface ArgContext {
@@ -20,8 +20,8 @@ interface ScopeBinding {
20
20
 
21
21
  interface TransformContext {
22
22
  bindings: Bindings;
23
+ checker?: ts.TypeChecker;
23
24
  computedArgRanges: Range[];
24
- hasReactiveImport: boolean;
25
25
  ns: string;
26
26
  replacements: Replacement[];
27
27
  scopedBindings: ScopeBinding[];
@@ -104,18 +104,28 @@ function isInScope(reference: ts.Node, binding: ScopeBinding): boolean {
104
104
  return false;
105
105
  }
106
106
 
107
- function isReactiveReassignment(node: ts.Node): boolean {
107
+ function isReactiveCall(node: ts.CallExpression, checker?: ts.TypeChecker): boolean {
108
+ if (!ts.isIdentifier(node.expression)) {
109
+ return false;
110
+ }
111
+
112
+ if (node.expression.text !== COMPILER_ENTRYPOINT) {
113
+ return false;
114
+ }
115
+
116
+ return imports.isFromPackage(node.expression, PACKAGE, checker);
117
+ }
118
+
119
+ function isReactiveReassignment(node: ts.Node, checker?: ts.TypeChecker): boolean {
108
120
  let parent = node.parent;
109
121
 
110
122
  if (
111
123
  ts.isBinaryExpression(parent) &&
112
124
  parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
113
125
  parent.right === node &&
114
- ts.isCallExpression(node) &&
115
- ts.isIdentifier((node as ts.CallExpression).expression) &&
116
- ((node as ts.CallExpression).expression as ts.Identifier).text === COMPILER_ENTRYPOINT
126
+ ts.isCallExpression(node)
117
127
  ) {
118
- return true;
128
+ return isReactiveCall(node as ts.CallExpression, checker);
119
129
  }
120
130
 
121
131
  return false;
@@ -149,28 +159,9 @@ function isWriteContext(node: ts.Identifier): 'simple' | 'compound' | 'increment
149
159
 
150
160
  function visit(ctx: TransformContext, node: ts.Node): void {
151
161
  if (
152
- ts.isImportDeclaration(node) &&
153
- ts.isStringLiteral(node.moduleSpecifier) &&
154
- node.moduleSpecifier.text.includes(PACKAGE)
155
- ) {
156
- let clause = node.importClause;
157
-
158
- if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
159
- for (let i = 0, n = clause.namedBindings.elements.length; i < n; i++) {
160
- if (clause.namedBindings.elements[i].name.text === COMPILER_ENTRYPOINT) {
161
- ctx.hasReactiveImport = true;
162
- break;
163
- }
164
- }
165
- }
166
- }
167
-
168
- if (
169
- ctx.hasReactiveImport &&
170
162
  ts.isCallExpression(node) &&
171
- ts.isIdentifier(node.expression) &&
172
- node.expression.text === COMPILER_ENTRYPOINT &&
173
- node.arguments.length > 0
163
+ node.arguments.length > 0 &&
164
+ isReactiveCall(node, ctx.checker)
174
165
  ) {
175
166
  let arg = node.arguments[0],
176
167
  classification: COMPILER_TYPES | null = COMPILER_TYPES.Signal;
@@ -237,6 +228,7 @@ function visit(ctx: TransformContext, node: ts.Node): void {
237
228
  }
238
229
  }
239
230
 
231
+
240
232
  if (ts.isIdentifier(node) && node.parent && !isInDeclarationInit(node.parent)) {
241
233
  if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
242
234
  ts.forEachChild(node, n => visit(ctx, n));
@@ -255,7 +247,7 @@ function visit(ctx: TransformContext, node: ts.Node): void {
255
247
 
256
248
  if (binding && node.parent) {
257
249
  if (
258
- !isReactiveReassignment(node.parent) &&
250
+ !isReactiveReassignment(node.parent, ctx.checker) &&
259
251
  !(ts.isTypeOfExpression(node.parent) && node.parent.expression === node)
260
252
  ) {
261
253
  let writeCtx = isWriteContext(node);
@@ -272,7 +264,7 @@ function visit(ctx: TransformContext, node: ts.Node): void {
272
264
  });
273
265
  }
274
266
  else if (writeCtx === 'compound' && ts.isBinaryExpression(parent)) {
275
- let op = COMPOUND_OPERATORS.get(parent.operatorToken.kind) ?? '+';
267
+ let op = COMPOUND_OPERATORS.get(parent.operatorToken.kind) ?? '+'
276
268
 
277
269
  ctx.replacements.push({
278
270
  end: parent.end,
@@ -324,6 +316,7 @@ function visit(ctx: TransformContext, node: ts.Node): void {
324
316
  ts.forEachChild(node, n => visit(ctx, n));
325
317
  }
326
318
 
319
+
327
320
  function visitArg(ctx: ArgContext, node: ts.Node): void {
328
321
  if (ts.isIdentifier(node) && node.parent) {
329
322
  if (
@@ -347,12 +340,12 @@ function visitArg(ctx: ArgContext, node: ts.Node): void {
347
340
  }
348
341
 
349
342
 
350
- export default (sourceFile: ts.SourceFile, bindings: Bindings, ns: string): string => {
343
+ export default (sourceFile: ts.SourceFile, bindings: Bindings, ns: string, checker?: ts.TypeChecker): string => {
351
344
  let code = sourceFile.getFullText(),
352
345
  ctx: TransformContext = {
353
346
  bindings,
347
+ checker,
354
348
  computedArgRanges: [],
355
- hasReactiveImport: false,
356
349
  ns,
357
350
  replacements: [],
358
351
  scopedBindings: [],
@@ -367,4 +360,5 @@ export default (sourceFile: ts.SourceFile, bindings: Bindings, ns: string): stri
367
360
  }
368
361
 
369
362
  return c.replace(code, ctx.replacements);
370
- };
363
+ };
364
+