@abide/abide 0.50.0 → 0.51.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.
@@ -0,0 +1,85 @@
1
+ import ts from 'typescript'
2
+
3
+ /*
4
+ Folds a cell-source `watch(source, handler)` into the auto-tracked thunk form
5
+ `watch(() => (handler)(source))`, so the reaction reads the cell reactively
6
+ instead of receiving a one-time value.
7
+
8
+ The plain-variable ergonomic makes a bare `count` a VALUE read everywhere the
9
+ read-lowering runs (`renameSignalRefs` → `docAccessTransformer`). That is right
10
+ for `{count}` / `count + 1`, but it also lowered `watch(count, handler)`'s source
11
+ to `$$model.read("count")` — a number — so the runtime `watch` (which expects a
12
+ `State` and reads `.value` inside an effect) never subscribed: the reaction was
13
+ inert. The `watch(count, n => …)` form was documented but silently dead.
14
+
15
+ This runs BEFORE the read-lowering, so the `source` moved inside the new thunk is
16
+ lowered to its normal reactive read there — one rewrite covers every cell kind
17
+ (doc-slot `state`, `.value` cell, `computed`, `linked`), because it reuses the
18
+ read the rest of the pipeline already emits. The runtime `watch(thunk)` is the
19
+ same auto-tracked effect the compiler's own bindings use, so semantics match the
20
+ old `effect(() => handler(cell.value))` cell branch exactly.
21
+
22
+ Only a bare cell reference (or an array literal of them — the `watch([a, b], …)`
23
+ form) is folded. A socket / rpc / arbitrary object source is left untouched so the
24
+ runtime's own source dispatch (`cache.on` / `reactToRpc`) still handles it; the
25
+ thunk (`watch(() => …)`) and rpc-with-args (`watch(fn, args, handler)`) forms are
26
+ already correct and are matched out by arity.
27
+ */
28
+ export function wrapReactionCellSources(
29
+ cellNames: ReadonlySet<string>,
30
+ watchLocalNames: ReadonlySet<string>,
31
+ ): ts.TransformerFactory<ts.SourceFile> {
32
+ /* A source that names a reactive cell: a bare cell identifier, or a non-empty array
33
+ literal whose every element is one (the multi-cell `watch([a, b], …)` form). */
34
+ function isCellSource(source: ts.Expression): boolean {
35
+ if (ts.isIdentifier(source)) {
36
+ return cellNames.has(source.text)
37
+ }
38
+ if (ts.isArrayLiteralExpression(source)) {
39
+ return (
40
+ source.elements.length > 0 &&
41
+ source.elements.every(
42
+ (element) => ts.isIdentifier(element) && cellNames.has(element.text),
43
+ )
44
+ )
45
+ }
46
+ return false
47
+ }
48
+
49
+ return (context) => (root) => {
50
+ function visit(node: ts.Node): ts.Node {
51
+ const [sourceArg, handlerArg] = ts.isCallExpression(node) ? node.arguments : []
52
+ if (
53
+ ts.isCallExpression(node) &&
54
+ ts.isIdentifier(node.expression) &&
55
+ watchLocalNames.has(node.expression.text) &&
56
+ node.arguments.length === 2 &&
57
+ sourceArg !== undefined &&
58
+ handlerArg !== undefined &&
59
+ isCellSource(sourceArg)
60
+ ) {
61
+ /* Visit the operands first — a handler body may itself contain a nested cell-source
62
+ watch, and the source stays a plain identifier the read-lowering rewrites next. */
63
+ const source = ts.visitNode(sourceArg, visit) as ts.Expression
64
+ const handler = ts.visitNode(handlerArg, visit) as ts.Expression
65
+ /* `(handler)(source)` — parenthesise the handler so an arrow callee prints callable. */
66
+ const call = ts.factory.createCallExpression(
67
+ ts.factory.createParenthesizedExpression(handler),
68
+ undefined,
69
+ [source],
70
+ )
71
+ const thunk = ts.factory.createArrowFunction(
72
+ undefined,
73
+ undefined,
74
+ [],
75
+ undefined,
76
+ ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
77
+ call,
78
+ )
79
+ return ts.factory.createCallExpression(node.expression, undefined, [thunk])
80
+ }
81
+ return ts.visitEachChild(node, visit, context)
82
+ }
83
+ return ts.visitNode(root, visit) as ts.SourceFile
84
+ }
85
+ }