@esportsplus/reactivity 0.37.1 → 0.38.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,55 +1,112 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
- import { imports } from '@esportsplus/typescript/compiler';
3
2
  import { ENTRYPOINT, NAMESPACE, PACKAGE_NAME } from './constants.js';
4
3
  import array from './array.js';
5
4
  import object from './object.js';
6
5
  import primitives from './primitives.js';
7
6
  import scope from './bindings.js';
8
- function collect(checker, node, calls) {
9
- if (isReactiveCallExpression(checker, node)) {
10
- calls.add(node);
7
+ function failed(sourceFile, failures) {
8
+ let lines = [];
9
+ for (let i = 0, n = failures.length; i < n; i++) {
10
+ let { message, node } = failures[i], position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
11
+ lines.push(` ${sourceFile.fileName}:${position.line + 1}:${position.character + 1} ${message}: ${node.getText(sourceFile).split('\n')[0]}`);
11
12
  }
12
- node.forEachChild(child => collect(checker, child, calls));
13
+ return new Error(`${PACKAGE_NAME}: code that must be compiled cannot be:\n${lines.join('\n')}`);
13
14
  }
14
- function isReactiveCallExpression(checker, node) {
15
- if (!ts.isCallExpression(node)) {
15
+ function isConstList(node) {
16
+ return node !== undefined && ts.isVariableDeclarationList(node) && (node.flags & ts.NodeFlags.Const) !== 0;
17
+ }
18
+ function isLowered(site) {
19
+ let parent = site.parent;
20
+ while (parent && ts.isParenthesizedExpression(parent)) {
21
+ site = parent;
22
+ parent = site.parent;
23
+ }
24
+ if (!parent) {
16
25
  return false;
17
26
  }
18
- let expr = node.expression;
19
- if (ts.isIdentifier(expr)) {
20
- return imports.includes(checker, expr, PACKAGE_NAME, ENTRYPOINT);
27
+ if (ts.isCallExpression(parent)) {
28
+ return parent.expression === site;
29
+ }
30
+ if (ts.isVariableDeclaration(parent)) {
31
+ return parent.initializer === site && ts.isIdentifier(parent.name) && isConstList(parent.parent);
21
32
  }
22
- if (ts.isPropertyAccessExpression(expr) && expr.name.text === ENTRYPOINT && ts.isIdentifier(expr.expression)) {
23
- return imports.includes(checker, expr.expression, PACKAGE_NAME);
33
+ if (ts.isBindingElement(parent) && parent.propertyName === site) {
34
+ let declaration = parent;
35
+ while (declaration && !ts.isVariableDeclaration(declaration)) {
36
+ declaration = declaration.parent;
37
+ }
38
+ return declaration !== undefined && isConstList(declaration.parent);
24
39
  }
25
40
  return false;
26
41
  }
42
+ function siteOf(identifier) {
43
+ let parent = identifier.parent;
44
+ if ((ts.isPropertyAccessExpression(parent) && parent.name === identifier) ||
45
+ (ts.isElementAccessExpression(parent) && parent.argumentExpression === identifier)) {
46
+ return parent;
47
+ }
48
+ return identifier;
49
+ }
27
50
  export default {
28
- patterns: ['reactive(', 'reactive<'],
29
51
  transform: (ctx) => {
30
- let checker = ctx.checker;
31
- if (!checker) {
32
- return {};
52
+ let { checker, program, sourceFile } = ctx, bindings = scope.create(checker, program, sourceFile), calls = new Set(), dependencies = new Set(), failures = [], self = sourceFile.fileName.toLowerCase();
53
+ for (let [identifier, found] of bindings.origins) {
54
+ let entry = scope.holds(bindings, found);
55
+ if (!entry && scope.kind(bindings, found) === null) {
56
+ continue;
57
+ }
58
+ for (let file of [...found.through, found.declaration.path]) {
59
+ if (file.toLowerCase() !== self) {
60
+ dependencies.add(file);
61
+ }
62
+ }
63
+ if (!entry) {
64
+ continue;
65
+ }
66
+ let site = siteOf(identifier), parent = site.parent;
67
+ while (parent && ts.isParenthesizedExpression(parent)) {
68
+ parent = parent.parent;
69
+ }
70
+ if (parent && ts.isCallExpression(parent) && scope.unwrap(parent.expression) === site) {
71
+ calls.add(parent);
72
+ }
73
+ else if (!isLowered(site)) {
74
+ failures.push({
75
+ message: `${ENTRYPOINT} is compiled away, so it can only be called (or aliased with const); it cannot be used as a value`,
76
+ node: site
77
+ });
78
+ }
33
79
  }
34
- let bindings = scope.create(checker), candidates = new Set(), intents = {
80
+ let isReactiveCall = (node) => calls.has(node), intents = {
35
81
  imports: [],
36
82
  prepend: [],
37
83
  replacements: []
38
- }, sourceFile = ctx.sourceFile;
39
- collect(checker, sourceFile, candidates);
40
- if (candidates.size === 0) {
41
- return {};
42
- }
43
- let isReactiveCall = (node) => candidates.has(node);
44
- let { calls, replacements } = primitives(sourceFile, bindings, isReactiveCall);
45
- intents.replacements.push(...replacements);
84
+ };
85
+ let lowered = primitives(sourceFile, bindings, isReactiveCall);
86
+ failures.push(...lowered.failures);
87
+ intents.replacements.push(...lowered.replacements);
46
88
  let objects = object(sourceFile, bindings, isReactiveCall);
47
89
  intents.prepend.push(...objects.prepend);
48
90
  intents.replacements.push(...objects.replacements);
49
- intents.replacements.push(...array(sourceFile, bindings, isReactiveCall));
91
+ let arrays = array(sourceFile, bindings, isReactiveCall);
92
+ failures.push(...arrays.failures);
93
+ intents.replacements.push(...arrays.replacements);
94
+ for (let i = 0, n = arrays.reactive.length; i < n; i++) {
95
+ let root = arrays.reactive[i];
96
+ while (ts.isPropertyAccessExpression(root) || ts.isElementAccessExpression(root) || ts.isParenthesizedExpression(root)) {
97
+ root = root.expression;
98
+ }
99
+ let found = ts.isIdentifier(root) ? scope.origin(bindings, root) : null;
100
+ if (found && found.declaration.path.toLowerCase() !== self) {
101
+ dependencies.add(found.declaration.path);
102
+ }
103
+ }
104
+ if (failures.length > 0) {
105
+ throw failed(sourceFile, failures);
106
+ }
50
107
  let transformed = new Set(intents.replacements.map(r => r.node));
51
- for (let i = 0, n = calls.length; i < n; i++) {
52
- let call = calls[i];
108
+ for (let i = 0, n = lowered.calls.length; i < n; i++) {
109
+ let call = lowered.calls[i];
53
110
  if (transformed.has(call) || transformed.has(call.expression)) {
54
111
  continue;
55
112
  }
@@ -58,11 +115,16 @@ export default {
58
115
  node: call
59
116
  });
60
117
  }
61
- intents.imports.push({
62
- namespace: NAMESPACE,
63
- package: PACKAGE_NAME,
64
- remove: [ENTRYPOINT]
65
- });
66
- return intents;
118
+ if (intents.replacements.length === 0 && intents.prepend.length === 0) {
119
+ return dependencies.size > 0 ? { dependencies: [...dependencies] } : {};
120
+ }
121
+ if (calls.size > 0 || lowered.replacements.length > 0 || intents.prepend.length > 0 || arrays.namespaced) {
122
+ intents.imports.push({
123
+ namespace: NAMESPACE,
124
+ package: PACKAGE_NAME,
125
+ remove: [ENTRYPOINT]
126
+ });
127
+ }
128
+ return { dependencies: [...dependencies], ...intents };
67
129
  }
68
130
  };
@@ -1,8 +1,8 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
2
  import { code, uid } from '@esportsplus/typescript/compiler';
3
3
  import { NAMESPACE, TYPES } from './constants.js';
4
- import scope from './bindings.js';
5
- function analyzeProperty(prop, sourceFile) {
4
+ function analyzeProperty(prop, bindings) {
5
+ let sourceFile = bindings.sourceFile;
6
6
  if (!ts.isPropertyAssignment(prop)) {
7
7
  return null;
8
8
  }
@@ -38,6 +38,10 @@ function analyzeProperty(prop, sourceFile) {
38
38
  }
39
39
  return { isStatic, key, type: TYPES.Array, valueText };
40
40
  }
41
+ let type = isStaticValue(unwrapped) ? undefined : bindings.checker.getTypeAtLocation(value);
42
+ if (type && (bindings.checker.isArrayType(type) || bindings.checker.isTupleType(type))) {
43
+ return { isStatic: false, key, type: TYPES.Array, valueText };
44
+ }
41
45
  return {
42
46
  isStatic: isStaticValue(value),
43
47
  key,
@@ -144,29 +148,22 @@ function visit(ctx, node) {
144
148
  if (ctx.isReactiveCall(node)) {
145
149
  let arg = node.arguments[0];
146
150
  if (arg && ts.isObjectLiteralExpression(arg)) {
147
- let properties = [], props = arg.properties, target = node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)
148
- ? node.parent.name
149
- : null, varname = target?.text ?? null;
151
+ let properties = [], props = arg.properties, varname = node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)
152
+ ? node.parent.name.text
153
+ : null;
150
154
  for (let i = 0, n = props.length; i < n; i++) {
151
155
  let prop = props[i];
152
156
  if (ts.isSpreadAssignment(prop)) {
153
157
  node.forEachChild(n => visit(ctx, n));
154
158
  return;
155
159
  }
156
- let analyzed = analyzeProperty(prop, ctx.sourceFile);
160
+ let analyzed = analyzeProperty(prop, ctx.bindings);
157
161
  if (!analyzed) {
158
162
  node.forEachChild(n => visit(ctx, n));
159
163
  return;
160
164
  }
161
165
  properties.push(analyzed);
162
166
  }
163
- if (target) {
164
- for (let i = 0, n = properties.length; i < n; i++) {
165
- if (properties[i].type === TYPES.Array) {
166
- scope.declarePath(ctx.bindings, target, properties[i].key);
167
- }
168
- }
169
- }
170
167
  ctx.calls.push({
171
168
  classname: uid('ReactiveObject'),
172
169
  node,
@@ -193,7 +190,7 @@ export default (sourceFile, bindings, isReactiveCall) => {
193
190
  replacements.push({
194
191
  generate: () => {
195
192
  let args = call.properties
196
- .filter(({ isStatic, type }) => typehint || !isStatic || type === TYPES.Computed)
193
+ .filter(({ isStatic, type }) => typehint || !isStatic || type !== TYPES.Signal)
197
194
  .map(p => p.valueText)
198
195
  .join(', ');
199
196
  return typehint
@@ -5,7 +5,21 @@ declare const _default: ({ root }?: {
5
5
  closeWatcher: () => void;
6
6
  configResolved: (config: unknown) => void;
7
7
  enforce: 'pre';
8
+ handleHotUpdate: <M>(ctx: {
9
+ file: string;
10
+ modules: M[];
11
+ server: {
12
+ moduleGraph: {
13
+ getModulesByFile(file: string): Set<M> | undefined;
14
+ invalidateModule(module: M, seen: Set<M>, timestamp: number, isHmr: boolean): void;
15
+ };
16
+ };
17
+ timestamp: number;
18
+ }) => M[] | undefined;
8
19
  name: string;
20
+ renderChunk: (code: string, chunk: {
21
+ fileName: string;
22
+ }) => null;
9
23
  transform: (code: string, id: string) => {
10
24
  code: string;
11
25
  map: import("@esportsplus/typescript/compiler").SourceMapV3;
@@ -1,9 +1,15 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
2
  import type { ReplacementIntent } from '@esportsplus/typescript/compiler';
3
3
  import type { Bindings, IsReactiveCall } from './types.js';
4
+ type Failure = {
5
+ message: string;
6
+ node: ts.Node;
7
+ };
4
8
  type PrimitivesTransformResult = {
5
9
  calls: ts.CallExpression[];
10
+ failures: Failure[];
6
11
  replacements: ReplacementIntent[];
7
12
  };
8
13
  export default _default;
9
14
  declare function _default(sourceFile: ts.SourceFile, bindings: Bindings, isReactiveCall: IsReactiveCall): PrimitivesTransformResult;
15
+ export type { Failure };
@@ -3,38 +3,33 @@ import { COMPOUND_OPERATORS, NAMESPACE, TYPES } from './constants.js';
3
3
  import scope from './bindings.js';
4
4
  function declare(ctx, node) {
5
5
  if (ctx.isReactiveCall(node)) {
6
- let call = node;
6
+ let call = node, kind = scope.classify(ctx.bindings, call);
7
7
  ctx.calls.push(call);
8
- if (call.arguments.length > 0) {
9
- let arg = call.arguments[0], classification = TYPES.Signal;
10
- if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
11
- classification = TYPES.Computed;
12
- }
13
- else {
14
- let unwrapped = arg;
15
- while (ts.isAsExpression(unwrapped) || ts.isParenthesizedExpression(unwrapped) || ts.isTypeAssertion(unwrapped)) {
16
- unwrapped = unwrapped.expression;
17
- }
18
- if (ts.isArrayLiteralExpression(unwrapped) || ts.isObjectLiteralExpression(unwrapped)) {
19
- classification = null;
20
- }
21
- else if (ts.isCallExpression(unwrapped) || ts.isIdentifier(unwrapped)) {
22
- classification = null;
23
- ctx.replacements.push({
24
- generate: () => `${NAMESPACE}.reactive`,
25
- node: call.expression
26
- });
27
- }
8
+ if (kind === null) {
9
+ ctx.failures.push({
10
+ message: 'reactive() takes one argument whose kind (value, computed, array or object) is provable; ' +
11
+ 'pass a literal or give the argument a concrete type (not any, unknown or a mixed union)',
12
+ node: call
13
+ });
14
+ }
15
+ else if (kind === TYPES.Signal || kind === TYPES.Computed) {
16
+ if (!isBound(call)) {
17
+ ctx.failures.push({
18
+ message: `a reactive ${kind === TYPES.Signal ? 'value' : 'computed'} must initialize a variable (or be a module's ` +
19
+ 'default export); anywhere else its signal is never read',
20
+ node: call
21
+ });
28
22
  }
29
- if (classification !== null) {
30
- let target = targetOf(call), type = classification;
31
- if (target) {
32
- scope.declare(ctx.bindings, target, type);
33
- }
23
+ ctx.replacements.push({
24
+ generate: () => kind === TYPES.Computed ? `${NAMESPACE}.computed` : `${NAMESPACE}.signal`,
25
+ node: call.expression
26
+ });
27
+ }
28
+ else {
29
+ let arg = scope.unwrap(call.arguments[0]);
30
+ if (!ts.isArrayLiteralExpression(arg) && !ts.isObjectLiteralExpression(arg)) {
34
31
  ctx.replacements.push({
35
- generate: () => type === TYPES.Computed
36
- ? `${NAMESPACE}.computed`
37
- : `${NAMESPACE}.signal`,
32
+ generate: () => `${NAMESPACE}.reactive`,
38
33
  node: call.expression
39
34
  });
40
35
  }
@@ -42,12 +37,33 @@ function declare(ctx, node) {
42
37
  }
43
38
  node.forEachChild(child => declare(ctx, child));
44
39
  }
45
- function isName(node, parent) {
46
- if (ts.isShorthandPropertyAssignment(parent)) {
40
+ function isBound(call) {
41
+ let node = call, parent = node.parent;
42
+ while (parent &&
43
+ (ts.isAsExpression(parent) || ts.isNonNullExpression(parent) || ts.isParenthesizedExpression(parent) || ts.isSatisfiesExpression(parent))) {
44
+ node = parent;
45
+ parent = node.parent;
46
+ }
47
+ if (!parent) {
47
48
  return false;
48
49
  }
49
- let slots = parent;
50
- return slots.name === node || slots.propertyName === node || slots.label === node;
50
+ if (ts.isVariableDeclaration(parent)) {
51
+ return parent.initializer === node && ts.isIdentifier(parent.name);
52
+ }
53
+ if (ts.isBinaryExpression(parent)) {
54
+ return parent.operatorToken.kind === ts.SyntaxKind.EqualsToken && parent.right === node && ts.isIdentifier(parent.left);
55
+ }
56
+ return ts.isExportAssignment(parent);
57
+ }
58
+ function isNamespace(bindings, expression) {
59
+ let value = scope.unwrap(expression);
60
+ if (ts.isAwaitExpression(value)) {
61
+ value = scope.unwrap(value.expression);
62
+ }
63
+ if (ts.isCallExpression(value)) {
64
+ return value.expression.kind === ts.SyntaxKind.ImportKeyword;
65
+ }
66
+ return ts.isIdentifier(value) && scope.origin(bindings, value)?.declaration.kind === ts.SyntaxKind.SourceFile;
51
67
  }
52
68
  function isPatternTarget(node) {
53
69
  let current = node;
@@ -67,24 +83,30 @@ function isPatternTarget(node) {
67
83
  return (ts.isBinaryExpression(parent) && parent.operatorToken.kind === ts.SyntaxKind.EqualsToken && parent.left === current) ||
68
84
  ((ts.isForOfStatement(parent) || ts.isForInStatement(parent)) && parent.initializer === current);
69
85
  }
70
- function reference(ctx, node) {
86
+ function readNode(identifier) {
87
+ let parent = identifier.parent;
88
+ if ((ts.isPropertyAccessExpression(parent) && parent.name === identifier) ||
89
+ (ts.isElementAccessExpression(parent) && parent.argumentExpression === identifier)) {
90
+ return parent;
91
+ }
92
+ return identifier;
93
+ }
94
+ function reference(ctx, node, kind) {
71
95
  let parent = node.parent;
72
- if (!parent || ts.isExportSpecifier(parent) || ts.isImportSpecifier(parent) || isName(node, parent)) {
96
+ if (!parent) {
73
97
  return;
74
98
  }
75
- let binding = scope.resolve(ctx.bindings, node);
76
- if (!binding || binding.type === TYPES.Array) {
99
+ if (ts.isBinaryExpression(parent) && parent.operatorToken.kind === ts.SyntaxKind.EqualsToken && parent.left === node && ctx.isReactiveCall(scope.unwrap(parent.right))) {
77
100
  return;
78
101
  }
79
- if ((ts.isBinaryExpression(parent) && parent.operatorToken.kind === ts.SyntaxKind.EqualsToken && parent.left === node && ctx.isReactiveCall(parent.right)) ||
80
- (ts.isTypeOfExpression(parent) && parent.expression === node) ||
81
- isPatternTarget(node)) {
102
+ if (isPatternTarget(node)) {
103
+ ctx.failures.push({ message: 'a reactive binding cannot be assigned through a destructuring pattern; assign it directly', node });
82
104
  return;
83
105
  }
84
- let name = node.text;
106
+ let text = node.getText(ctx.bindings.sourceFile);
85
107
  if (ts.isShorthandPropertyAssignment(parent)) {
86
108
  ctx.replacements.push({
87
- generate: () => `${name}: ${NAMESPACE}.read(${name})`,
109
+ generate: () => `${text}: ${NAMESPACE}.read(${text})`,
88
110
  node: parent
89
111
  });
90
112
  return;
@@ -107,25 +129,26 @@ function reference(ctx, node) {
107
129
  }
108
130
  if (!writeCtx) {
109
131
  ctx.replacements.push({
110
- generate: () => `${NAMESPACE}.read(${name})`,
132
+ generate: () => `${NAMESPACE}.read(${text})`,
111
133
  node
112
134
  });
113
135
  return;
114
136
  }
115
- if (binding.type === TYPES.Computed) {
137
+ if (kind === TYPES.Computed) {
138
+ ctx.failures.push({ message: 'a reactive computed is read-only', node });
116
139
  return;
117
140
  }
118
141
  if (writeCtx === 'simple' && ts.isBinaryExpression(parent)) {
119
142
  let right = parent.right;
120
143
  ctx.replacements.push({
121
- generate: (sf) => `${NAMESPACE}.write(${name}, ${right.getText(sf)})`,
144
+ generate: (sf) => `${NAMESPACE}.write(${text}, ${right.getText(sf)})`,
122
145
  node: parent
123
146
  });
124
147
  }
125
148
  else if (writeCtx === 'compound' && ts.isBinaryExpression(parent)) {
126
149
  let op = COMPOUND_OPERATORS.get(parent.operatorToken.kind) ?? '+', right = parent.right;
127
150
  ctx.replacements.push({
128
- generate: (sf) => `${NAMESPACE}.write(${name}, ${name}.value ${op} ${right.getText(sf)})`,
151
+ generate: (sf) => `${NAMESPACE}.write(${text}, ${text}.value ${op} ${right.getText(sf)})`,
129
152
  node: parent
130
153
  });
131
154
  }
@@ -133,54 +156,63 @@ function reference(ctx, node) {
133
156
  let unary = parent, delta = unary.operator === ts.SyntaxKind.PlusPlusToken ? '+ 1' : '- 1';
134
157
  if (ts.isExpressionStatement(unary.parent)) {
135
158
  ctx.replacements.push({
136
- generate: () => `${NAMESPACE}.write(${name}, ${name}.value ${delta})`,
159
+ generate: () => `${NAMESPACE}.write(${text}, ${text}.value ${delta})`,
137
160
  node: unary
138
161
  });
139
162
  }
140
163
  else if (ts.isPrefixUnaryExpression(unary)) {
141
164
  ctx.replacements.push({
142
- generate: () => `(${NAMESPACE}.write(${name}, ${name}.value ${delta}), ${name}.value)`,
165
+ generate: () => `(${NAMESPACE}.write(${text}, ${text}.value ${delta}), ${text}.value)`,
143
166
  node: unary
144
167
  });
145
168
  }
146
169
  else {
147
170
  let tmp = `_t${ctx.tmpCounter++}`;
148
171
  ctx.replacements.push({
149
- generate: () => `((${tmp}) => (${NAMESPACE}.write(${name}, ${tmp} ${delta}), ${tmp}))(${name}.value)`,
172
+ generate: () => `((${tmp}) => (${NAMESPACE}.write(${text}, ${tmp} ${delta}), ${tmp}))(${text}.value)`,
150
173
  node: unary
151
174
  });
152
175
  }
153
176
  }
154
177
  }
155
- function rewrite(ctx, node) {
156
- if (ts.isTypeNode(node)) {
157
- return;
158
- }
159
- if (ts.isIdentifier(node)) {
160
- reference(ctx, node);
161
- return;
178
+ function rewrite(ctx) {
179
+ let { bindings } = ctx;
180
+ for (let [identifier, found] of bindings.origins) {
181
+ let kind = scope.kind(bindings, found);
182
+ if (kind !== TYPES.Signal && kind !== TYPES.Computed) {
183
+ continue;
184
+ }
185
+ let parent = identifier.parent;
186
+ if (ts.isBindingElement(parent) && parent.propertyName === identifier) {
187
+ ctx.failures.push({ message: 'a reactive binding cannot be destructured (it would copy its signal); read it through the namespace', node: identifier });
188
+ continue;
189
+ }
190
+ reference(ctx, readNode(identifier), kind);
162
191
  }
163
- node.forEachChild(child => rewrite(ctx, child));
192
+ unpacked(ctx, bindings.sourceFile);
164
193
  }
165
- function targetOf(call) {
166
- let parent = call.parent;
167
- if (parent && ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) {
168
- return parent.name;
169
- }
170
- if (parent && ts.isBinaryExpression(parent) && parent.operatorToken.kind === ts.SyntaxKind.EqualsToken && ts.isIdentifier(parent.left)) {
171
- return parent.left;
194
+ function unpacked(ctx, node) {
195
+ if (ts.isBindingElement(node) && !node.propertyName && ts.isObjectBindingPattern(node.parent)) {
196
+ let initializer = node.parent.parent.initializer;
197
+ if (initializer && isNamespace(ctx.bindings, initializer)) {
198
+ let kind = scope.kind(ctx.bindings, scope.origin(ctx.bindings, node));
199
+ if (kind === TYPES.Signal || kind === TYPES.Computed) {
200
+ ctx.failures.push({ message: 'a reactive binding cannot be destructured (it would copy its signal); read it through the namespace', node });
201
+ }
202
+ }
172
203
  }
173
- return null;
204
+ node.forEachChild(child => unpacked(ctx, child));
174
205
  }
175
206
  export default (sourceFile, bindings, isReactiveCall) => {
176
207
  let ctx = {
177
208
  bindings,
178
209
  calls: [],
210
+ failures: [],
179
211
  isReactiveCall,
180
212
  replacements: [],
181
213
  tmpCounter: 0
182
214
  };
183
215
  declare(ctx, sourceFile);
184
- rewrite(ctx, sourceFile);
185
- return { calls: ctx.calls, replacements: ctx.replacements };
216
+ rewrite(ctx);
217
+ return { calls: ctx.calls, failures: ctx.failures, replacements: ctx.replacements };
186
218
  };
@@ -1,10 +1,11 @@
1
1
  import type { ts } from '@esportsplus/typescript';
2
- import type { TYPES } from './constants.js';
2
+ import type { Origin } from '@esportsplus/typescript/compiler';
3
3
  type Bindings = {
4
4
  checker: ts.Checker;
5
- names: Set<string>;
6
- paths: Map<ts.Symbol, Set<string>>;
7
- symbols: Map<ts.Symbol, TYPES>;
5
+ entries: Set<string>;
6
+ origins: Map<ts.Node, Origin>;
7
+ program: ts.Program;
8
+ sourceFile: ts.SourceFile;
8
9
  };
9
10
  type IsReactiveCall = (node: ts.Node) => node is ts.CallExpression;
10
11
  export type { Bindings, IsReactiveCall };
package/build/types.d.ts CHANGED
@@ -33,7 +33,7 @@ interface Link {
33
33
  declare const READONLY: unique symbol;
34
34
  type Reactive<T> = T extends (...args: never[]) => infer R ? Settled<R> & {
35
35
  readonly [READONLY]: true;
36
- } : T extends (infer U)[] ? U[] & Pick<ReactiveArray<U>, 'clear' | 'dispose' | 'on' | 'once'> : T extends Record<PropertyKey, unknown> ? {
36
+ } : T extends (infer U)[] ? U[] & Pick<ReactiveArray<U>, '$length' | '$set' | 'clear' | 'dispose' | 'on' | 'once'> : T extends Record<PropertyKey, unknown> ? {
37
37
  [K in keyof T]: T[K] extends (infer U)[] ? Reactive<U[]> : T[K];
38
38
  } & {
39
39
  dispose: VoidFunction;
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "author": "ICJR",
3
3
  "dependencies": {
4
- "@esportsplus/utilities": "^0.28.4"
4
+ "@esportsplus/utilities": "^0.28.6"
5
5
  },
6
6
  "devDependencies": {
7
- "@esportsplus/typescript": "^0.36.0",
7
+ "@esportsplus/typescript": "^0.37.0",
8
8
  "@types/node": "^26.4.1",
9
9
  "vite": "^8.2.2",
10
10
  "vitest": "^5.0.0"
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "type": "module",
38
38
  "types": "build/index.d.ts",
39
- "version": "0.37.1",
39
+ "version": "0.38.0",
40
40
  "scripts": {
41
41
  "agent:bench": "vitest bench --run",
42
42
  "agent:test": "tsc --noEmit && vitest run",
@@ -1,8 +1,8 @@
1
1
  allowBuilds:
2
2
  esbuild: true
3
3
  minimumReleaseAgeExclude:
4
- - '@esportsplus/utilities@0.28.1 || 0.28.3 || 0.28.4'
5
- - '@esportsplus/typescript@0.31.0 || 0.32.0 || 0.33.0 || 0.33.2 || 0.33.4 || 0.34.1 || 0.34.3 || 0.34.4 || 0.36.0'
4
+ - '@esportsplus/utilities@0.28.1 || 0.28.3 || 0.28.4 || 0.28.6'
5
+ - '@esportsplus/typescript@0.31.0 || 0.32.0 || 0.33.0 || 0.33.2 || 0.33.4 || 0.34.1 || 0.34.3 || 0.34.4 || 0.36.0 || 0.36.1 || 0.37.0'
6
6
  - tsc-alias@1.9.4
7
7
  - '@vitest/mocker@5.0.0'
8
8
  - '@vitest/spy@5.0.0'