octane 0.2.11 → 0.2.13

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.
Files changed (264) hide show
  1. package/README.md +5 -0
  2. package/dist/behavior-root.d.ts +13 -3
  3. package/dist/behavior-root.js +28 -5
  4. package/dist/cjs/behavior-root.cjs +32 -6
  5. package/dist/cjs/class-names.cjs +49 -0
  6. package/dist/cjs/constants.cjs +9 -0
  7. package/dist/cjs/context-epoch.cjs +38 -0
  8. package/dist/cjs/css.cjs +6 -27
  9. package/dist/cjs/data-encoding.cjs +160 -0
  10. package/dist/cjs/dom-binding-handoff.cjs +93 -0
  11. package/dist/cjs/dom-binding-protocol.cjs +115 -0
  12. package/dist/cjs/dom-binding-styles.cjs +292 -0
  13. package/dist/cjs/dom-bindings.cjs +700 -0
  14. package/dist/cjs/dom-focused-move.cjs +66 -0
  15. package/dist/cjs/dom-stage.cjs +896 -0
  16. package/dist/cjs/dom-tables.cjs +4 -94
  17. package/dist/cjs/error-codes.client.generated.cjs +33 -0
  18. package/dist/cjs/error-codes.server.generated.cjs +24 -0
  19. package/dist/cjs/hydration/control-capture.cjs +244 -0
  20. package/dist/cjs/hydration/event-capture.cjs +125 -43
  21. package/dist/cjs/hydration/interaction-config.cjs +50 -2
  22. package/dist/cjs/hydration-markers.cjs +37 -0
  23. package/dist/cjs/independent-hydration-protocol.cjs +106 -0
  24. package/dist/cjs/index.cjs +9 -0
  25. package/dist/cjs/runtime.cjs +6397 -1683
  26. package/dist/cjs/runtime.server.cjs +1465 -213
  27. package/dist/cjs/server/early-signals.cjs +45 -0
  28. package/dist/cjs/server/index.cjs +47 -0
  29. package/dist/cjs/server/rpc-batch.cjs +149 -0
  30. package/dist/cjs/server/rpc-stream.cjs +146 -0
  31. package/dist/cjs/server/rpc.cjs +34 -15
  32. package/dist/cjs/server/signal-query-observation.cjs +158 -0
  33. package/dist/cjs/server/streamed-region.cjs +52 -0
  34. package/dist/cjs/server/streamed-signals.cjs +512 -0
  35. package/dist/cjs/server-call.cjs +103 -0
  36. package/dist/cjs/server-rpc-batch-client.cjs +141 -0
  37. package/dist/cjs/server-rpc-client.cjs +94 -27
  38. package/dist/cjs/server-rpc-protocol.cjs +141 -0
  39. package/dist/cjs/server-rpc-stream-client.cjs +118 -0
  40. package/dist/cjs/signals/actions.cjs +474 -0
  41. package/dist/cjs/signals/client.cjs +4 -1
  42. package/dist/cjs/signals/computations.cjs +460 -0
  43. package/dist/cjs/signals/control-binding.cjs +368 -0
  44. package/dist/cjs/signals/derived-facade.cjs +50 -0
  45. package/dist/cjs/signals/document-owner.cjs +71 -0
  46. package/dist/cjs/signals/early-values.cjs +155 -0
  47. package/dist/cjs/signals/encoding.cjs +6 -95
  48. package/dist/cjs/signals/engine.cjs +276 -12
  49. package/dist/cjs/signals/errors.cjs +18 -0
  50. package/dist/cjs/signals/facade.cjs +477 -0
  51. package/dist/cjs/signals/graph.cjs +166 -20
  52. package/dist/cjs/signals/handle-protocol.cjs +36 -0
  53. package/dist/cjs/signals/index.cjs +81 -2
  54. package/dist/cjs/signals/native-read-client.cjs +10 -0
  55. package/dist/cjs/signals/owner-context.cjs +112 -0
  56. package/dist/cjs/signals/query-attempt-observer.cjs +68 -0
  57. package/dist/cjs/signals/query-facade.cjs +72 -0
  58. package/dist/cjs/signals/read-protocol.cjs +54 -0
  59. package/dist/cjs/signals/requests.cjs +287 -14
  60. package/dist/cjs/signals/scalar-computations.cjs +76 -0
  61. package/dist/cjs/signals/server.cjs +4 -1
  62. package/dist/cjs/signals/transition-candidate.cjs +567 -0
  63. package/dist/cjs/signals/transition-state.cjs +106 -0
  64. package/dist/cjs/signals/types.cjs +50 -0
  65. package/dist/cjs/stream-protocol.cjs +3 -0
  66. package/dist/cjs/streamed-signals-protocol.cjs +159 -0
  67. package/dist/cjs/style-values.cjs +122 -0
  68. package/dist/cjs/trusted-html.cjs +30 -0
  69. package/dist/cjs/version.cjs +1 -1
  70. package/dist/class-names.d.ts +5 -0
  71. package/dist/class-names.js +25 -0
  72. package/dist/compiler/bundler.js +51 -22
  73. package/dist/compiler/compile.js +2534 -581
  74. package/dist/compiler/dom-binding-program.js +935 -0
  75. package/dist/compiler/dom-binding-request.js +57 -0
  76. package/dist/compiler/dom-bindings.js +2245 -0
  77. package/dist/compiler/hook-deps.js +759 -10
  78. package/dist/compiler/hydrate-boundaries.js +358 -15
  79. package/dist/compiler/index.d.ts +39 -2
  80. package/dist/compiler/index.js +1 -0
  81. package/dist/compiler/native-attribute-reads.js +127 -0
  82. package/dist/compiler/native-change-diagnostics.js +32 -38
  83. package/dist/compiler/native-read-diagnostics.js +69 -8
  84. package/dist/compiler/native-read-types.js +80 -34
  85. package/dist/compiler/parser.node.js +1 -1
  86. package/dist/compiler/renderer-boundaries.js +40 -0
  87. package/dist/compiler/server-context.js +118 -0
  88. package/dist/compiler/signal-attempt-reads.js +479 -0
  89. package/dist/compiler/signal-declarations.js +557 -0
  90. package/dist/compiler/signal-start-reads.js +413 -0
  91. package/dist/compiler/slot-hooks.d.ts +3 -1
  92. package/dist/compiler/slot-hooks.js +168 -34
  93. package/dist/compiler/strong-auto-memo.js +90 -0
  94. package/dist/compiler/strong-html.js +143 -0
  95. package/dist/compiler/strong-mode.js +764 -66
  96. package/dist/compiler/strong-template-policy.js +307 -0
  97. package/dist/compiler/template-ir.js +200 -0
  98. package/dist/compiler/typescript.d.ts +2 -0
  99. package/dist/compiler/typescript.js +2 -0
  100. package/dist/compiler/vite.d.ts +3 -1
  101. package/dist/compiler/vite.js +122 -4
  102. package/dist/compiler/volar.LICENSES.txt +1 -1
  103. package/dist/compiler/volar.d.ts +6 -1
  104. package/dist/compiler/volar.js +750 -1433
  105. package/dist/constants.d.ts +1 -0
  106. package/dist/constants.js +10 -0
  107. package/dist/context-epoch.d.ts +2 -0
  108. package/dist/context-epoch.js +13 -0
  109. package/dist/css.d.ts +2 -5
  110. package/dist/css.js +2 -23
  111. package/dist/data-encoding.d.ts +9 -0
  112. package/dist/data-encoding.js +134 -0
  113. package/dist/dom-binding-classes.d.ts +15 -0
  114. package/dist/dom-binding-classes.js +129 -0
  115. package/dist/dom-binding-controls.d.ts +2 -0
  116. package/dist/dom-binding-controls.js +6 -0
  117. package/dist/dom-binding-handoff.d.ts +55 -0
  118. package/dist/dom-binding-handoff.js +62 -0
  119. package/dist/dom-binding-program.d.ts +300 -0
  120. package/dist/dom-binding-program.js +1625 -0
  121. package/dist/dom-binding-projections.d.ts +15 -0
  122. package/dist/dom-binding-projections.js +187 -0
  123. package/dist/dom-binding-protocol.d.ts +13 -0
  124. package/dist/dom-binding-protocol.js +88 -0
  125. package/dist/dom-binding-signals.d.ts +21 -0
  126. package/dist/dom-binding-signals.js +109 -0
  127. package/dist/dom-binding-styles.d.ts +17 -0
  128. package/dist/dom-binding-styles.js +272 -0
  129. package/dist/dom-bindings.d.ts +89 -0
  130. package/dist/dom-bindings.js +670 -0
  131. package/dist/dom-focused-move.d.ts +6 -0
  132. package/dist/dom-focused-move.js +42 -0
  133. package/dist/dom-stage.d.ts +52 -0
  134. package/dist/dom-stage.js +872 -0
  135. package/dist/dom-tables.d.ts +1 -3
  136. package/dist/dom-tables.js +1 -91
  137. package/dist/error-codes.client.generated.d.ts +9 -0
  138. package/dist/error-codes.client.generated.js +33 -0
  139. package/dist/error-codes.server.generated.d.ts +6 -0
  140. package/dist/error-codes.server.generated.js +24 -0
  141. package/dist/hydration/control-capture.d.ts +62 -0
  142. package/dist/hydration/control-capture.js +223 -0
  143. package/dist/hydration/document-lifecycle.d.ts +23 -0
  144. package/dist/hydration/document-lifecycle.js +80 -0
  145. package/dist/hydration/event-capture.d.ts +20 -1
  146. package/dist/hydration/event-capture.js +128 -42
  147. package/dist/hydration/independent-island.d.ts +45 -0
  148. package/dist/hydration/independent-island.js +233 -0
  149. package/dist/hydration/index.d.ts +8 -1
  150. package/dist/hydration/index.js +55 -1
  151. package/dist/hydration/interaction-config.d.ts +5 -0
  152. package/dist/hydration/interaction-config.js +44 -1
  153. package/dist/hydration/stream-delivery.d.ts +32 -0
  154. package/dist/hydration/stream-delivery.js +333 -0
  155. package/dist/hydration/stream-receiver.d.ts +26 -0
  156. package/dist/hydration/stream-receiver.js +204 -0
  157. package/dist/hydration/stream-result-receiver.d.ts +70 -0
  158. package/dist/hydration/stream-result-receiver.js +246 -0
  159. package/dist/hydration/streamed-signals.d.ts +44 -0
  160. package/dist/hydration/streamed-signals.js +186 -0
  161. package/dist/hydration/types.d.ts +6 -0
  162. package/dist/hydration-markers.d.ts +7 -0
  163. package/dist/hydration-markers.js +10 -0
  164. package/dist/independent-hydration-protocol.d.ts +31 -0
  165. package/dist/independent-hydration-protocol.js +80 -0
  166. package/dist/index.d.ts +3 -0
  167. package/dist/index.js +6 -0
  168. package/dist/internal/signal-read.d.ts +1 -0
  169. package/dist/internal/signal-read.js +4 -0
  170. package/dist/jsx-runtime-strong.d.ts +33 -0
  171. package/dist/jsx-runtime.d.ts +58 -2
  172. package/dist/public-types.d.ts +15 -1
  173. package/dist/runtime.d.ts +297 -23
  174. package/dist/runtime.js +6405 -1679
  175. package/dist/runtime.server.d.ts +110 -27
  176. package/dist/runtime.server.js +1458 -213
  177. package/dist/server/early-signals.d.ts +14 -0
  178. package/dist/server/early-signals.js +34 -0
  179. package/dist/server/index.d.ts +11 -2
  180. package/dist/server/index.js +50 -1
  181. package/dist/server/rpc-batch.d.ts +17 -0
  182. package/dist/server/rpc-batch.js +129 -0
  183. package/dist/server/rpc-stream.d.ts +9 -0
  184. package/dist/server/rpc-stream.js +125 -0
  185. package/dist/server/rpc.d.ts +5 -1
  186. package/dist/server/rpc.js +37 -13
  187. package/dist/server/signal-query-observation.d.ts +3 -0
  188. package/dist/server/signal-query-observation.js +134 -0
  189. package/dist/server/streamed-region.d.ts +15 -0
  190. package/dist/server/streamed-region.js +30 -0
  191. package/dist/server/streamed-signals.d.ts +45 -0
  192. package/dist/server/streamed-signals.js +488 -0
  193. package/dist/server-call.d.ts +41 -0
  194. package/dist/server-call.js +75 -0
  195. package/dist/server-rpc-batch-client.d.ts +7 -0
  196. package/dist/server-rpc-batch-client.js +119 -0
  197. package/dist/server-rpc-client.d.ts +3 -1
  198. package/dist/server-rpc-client.js +99 -27
  199. package/dist/server-rpc-protocol.d.ts +47 -0
  200. package/dist/server-rpc-protocol.js +109 -0
  201. package/dist/server-rpc-stream-client.d.ts +5 -0
  202. package/dist/server-rpc-stream-client.js +98 -0
  203. package/dist/signals/actions.d.ts +12 -0
  204. package/dist/signals/actions.js +466 -0
  205. package/dist/signals/client.d.ts +2 -0
  206. package/dist/signals/client.js +1 -0
  207. package/dist/signals/computations.d.ts +37 -0
  208. package/dist/signals/computations.js +455 -0
  209. package/dist/signals/control-binding.d.ts +31 -0
  210. package/dist/signals/control-binding.js +353 -0
  211. package/dist/signals/derived-facade.d.ts +3 -0
  212. package/dist/signals/derived-facade.js +25 -0
  213. package/dist/signals/document-owner.d.ts +10 -0
  214. package/dist/signals/document-owner.js +43 -0
  215. package/dist/signals/early-values.d.ts +24 -0
  216. package/dist/signals/early-values.js +124 -0
  217. package/dist/signals/encoding.d.ts +1 -3
  218. package/dist/signals/encoding.js +3 -93
  219. package/dist/signals/engine.d.ts +53 -4
  220. package/dist/signals/engine.js +275 -16
  221. package/dist/signals/errors.d.ts +7 -0
  222. package/dist/signals/errors.js +16 -0
  223. package/dist/signals/facade.d.ts +74 -0
  224. package/dist/signals/facade.js +452 -0
  225. package/dist/signals/graph.d.ts +52 -6
  226. package/dist/signals/graph.js +174 -19
  227. package/dist/signals/handle-protocol.d.ts +4 -0
  228. package/dist/signals/handle-protocol.js +11 -0
  229. package/dist/signals/index.d.ts +12 -3
  230. package/dist/signals/index.js +71 -2
  231. package/dist/signals/native-read-client.d.ts +3 -0
  232. package/dist/signals/native-read-client.js +10 -0
  233. package/dist/signals/owner-context.d.ts +22 -0
  234. package/dist/signals/owner-context.js +77 -0
  235. package/dist/signals/query-attempt-observer.d.ts +49 -0
  236. package/dist/signals/query-attempt-observer.js +41 -0
  237. package/dist/signals/query-facade.d.ts +3 -0
  238. package/dist/signals/query-facade.js +49 -0
  239. package/dist/signals/read-protocol.d.ts +37 -2
  240. package/dist/signals/read-protocol.js +44 -0
  241. package/dist/signals/requests.d.ts +38 -5
  242. package/dist/signals/requests.js +298 -15
  243. package/dist/signals/scalar-computations.d.ts +2 -0
  244. package/dist/signals/scalar-computations.js +60 -0
  245. package/dist/signals/server.d.ts +2 -0
  246. package/dist/signals/server.js +1 -0
  247. package/dist/signals/transition-candidate.d.ts +71 -0
  248. package/dist/signals/transition-candidate.js +557 -0
  249. package/dist/signals/transition-state.d.ts +51 -0
  250. package/dist/signals/transition-state.js +70 -0
  251. package/dist/signals/types.d.ts +95 -5
  252. package/dist/signals/types.js +20 -0
  253. package/dist/stream-protocol.d.ts +0 -5
  254. package/dist/stream-protocol.js +3 -0
  255. package/dist/streamed-signals-protocol.d.ts +60 -0
  256. package/dist/streamed-signals-protocol.js +130 -0
  257. package/dist/style-values.d.ts +3 -0
  258. package/dist/style-values.js +96 -0
  259. package/dist/trusted-html.d.ts +17 -0
  260. package/dist/trusted-html.js +6 -0
  261. package/dist/universal-core.d.ts +6 -0
  262. package/dist/universal-core.js +214 -202
  263. package/dist/version.js +1 -1
  264. package/package.json +48 -5
@@ -0,0 +1,413 @@
1
+ import { builders as b } from '@tsrx/core';
2
+ import { createLexicalAnalysis } from './compile-universal.js';
3
+ import { inheritHookMemoOrigin } from './inline-hook-memo.js';
4
+
5
+ const SIGNAL_MODULES = new Set([
6
+ 'octane/signals',
7
+ 'octane/signals/client',
8
+ 'octane/signals/server',
9
+ ]);
10
+ const METADATA = new Set([
11
+ 'loc',
12
+ 'start',
13
+ 'end',
14
+ 'range',
15
+ 'metadata',
16
+ 'parent',
17
+ 'comments',
18
+ 'leadingComments',
19
+ 'trailingComments',
20
+ 'innerComments',
21
+ ]);
22
+
23
+ function children(node, visit) {
24
+ for (const key in node) {
25
+ if (METADATA.has(key) || key.startsWith('_octane')) continue;
26
+ const value = node[key];
27
+ if (Array.isArray(value)) {
28
+ for (const child of value) if (child?.type) visit(child);
29
+ } else if (value?.type) visit(value);
30
+ }
31
+ }
32
+
33
+ function unwrap(node) {
34
+ while (
35
+ [
36
+ 'TSAsExpression',
37
+ 'TSTypeAssertion',
38
+ 'TSSatisfiesExpression',
39
+ 'TSNonNullExpression',
40
+ 'ParenthesizedExpression',
41
+ ].includes(node?.type)
42
+ )
43
+ node = node.expression;
44
+ return node;
45
+ }
46
+
47
+ function isFunction(node) {
48
+ return ['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression'].includes(
49
+ node.type,
50
+ );
51
+ }
52
+
53
+ function hasRender(node) {
54
+ if (node.type.startsWith('JSX')) return true;
55
+ if (isFunction(node)) return false;
56
+ let found = false;
57
+ children(node, (child) => {
58
+ found ||= hasRender(child);
59
+ });
60
+ return found;
61
+ }
62
+
63
+ /**
64
+ * Start adjacent strict reads or complete static native output reads of
65
+ * lexically proven immutable descriptors.
66
+ * Original reads remain the suspension/error/observation points. In particular,
67
+ * this does not hoist declarations, evaluate member receivers, cross statements,
68
+ * or enter a conditional arm before its authored control-flow boundary.
69
+ */
70
+ export function startIndependentSignalReads(ast, isText = null) {
71
+ if (
72
+ !(ast.body ?? []).some(
73
+ (node) => node.type === 'ImportDeclaration' && SIGNAL_MODULES.has(node.source?.value),
74
+ )
75
+ )
76
+ return ast;
77
+ const lexical = createLexicalAnalysis(ast);
78
+ const imports = new Map();
79
+ const declarations = new Map();
80
+ const names = new Set();
81
+ const binding = (node) =>
82
+ node?.type === 'Identifier'
83
+ ? lexical.resolveBinding(lexical.nodeScopes.get(node) ?? lexical.rootScope, node.name)
84
+ : null;
85
+ const recordFor = (node) => declarations.get(binding(node)?.scope)?.get(node.name);
86
+ function register(id, record, declarationScope) {
87
+ if (id?.type !== 'Identifier') return;
88
+ const scope = declarationScope
89
+ ? lexical.resolveBinding(declarationScope, id.name)?.scope
90
+ : binding(id)?.scope;
91
+ if (!scope) return;
92
+ let records = declarations.get(scope);
93
+ if (!records) declarations.set(scope, (records = new Map()));
94
+ records.set(id.name, record);
95
+ }
96
+ function collect(node, ownerFunction = null) {
97
+ if (node.type === 'Identifier') names.add(node.name);
98
+ if (
99
+ node.type === 'ImportDeclaration' &&
100
+ node.importKind !== 'type' &&
101
+ SIGNAL_MODULES.has(node.source?.value)
102
+ ) {
103
+ for (const specifier of node.specifiers ?? []) {
104
+ if (specifier.importKind === 'type') continue;
105
+ const factory = specifier.imported?.name ?? specifier.imported?.value;
106
+ if (
107
+ factory === 'query$' ||
108
+ factory === 'derived$' ||
109
+ specifier.type === 'ImportNamespaceSpecifier'
110
+ )
111
+ imports.set(specifier.local.name, {
112
+ factory,
113
+ source: node.source.value,
114
+ declaration: node,
115
+ });
116
+ }
117
+ }
118
+ if (node.type === 'VariableDeclaration') {
119
+ for (const declaration of node.declarations)
120
+ register(declaration.id, {
121
+ init: declaration.init,
122
+ kind: node.kind,
123
+ declaration,
124
+ ownerFunction,
125
+ });
126
+ } else if (node.type === 'FunctionDeclaration')
127
+ register(
128
+ node.id,
129
+ { init: node, kind: 'function', declaration: node },
130
+ lexical.nodeScopes.get(node),
131
+ );
132
+ children(node, (child) => collect(child, isFunction(node) ? node : ownerFunction));
133
+ }
134
+ collect(ast);
135
+ // Initializer reachability is insufficient when a selector was reassigned or
136
+ // installed in a mutable object. Fence any result captured by an already
137
+ // available closure, without attempting heap or assignment reconstruction.
138
+ const capturedResults = new Map();
139
+ const opaqueFunctions = new Set();
140
+ function collectCaptures(node, stack = []) {
141
+ const nested =
142
+ isFunction(node) || node.type === 'ClassDeclaration' || node.type === 'ClassExpression'
143
+ ? [...stack, node]
144
+ : stack;
145
+ if (node.type === 'Identifier' && !lexical.bindingNodes.has(node)) {
146
+ const record = recordFor(node);
147
+ const owner = record?.ownerFunction;
148
+ const index = owner ? nested.indexOf(owner) : -1;
149
+ const closure = index < 0 ? null : nested[index + 1];
150
+ if (closure) {
151
+ let captured = capturedResults.get(record);
152
+ if (!captured) capturedResults.set(record, (captured = new Set()));
153
+ captured.add(closure);
154
+ }
155
+ }
156
+ const callee = unwrap(node.callee);
157
+ if (
158
+ (node.type === 'CallExpression' || node.type === 'NewExpression') &&
159
+ callee?.type === 'Identifier' &&
160
+ (callee.name === 'eval' || callee.name === 'Function')
161
+ ) {
162
+ for (const owner of nested) opaqueFunctions.add(owner);
163
+ }
164
+ children(node, (child) => collectCaptures(child, nested));
165
+ }
166
+ collectCaptures(ast);
167
+ function capturedBefore(result, position) {
168
+ for (const closure of capturedResults.get(recordFor(result)) ?? []) {
169
+ if (
170
+ closure.type === 'FunctionDeclaration' ||
171
+ (closure.start ?? -Infinity) < (position ?? Infinity)
172
+ )
173
+ return true;
174
+ }
175
+ return false;
176
+ }
177
+ function factoryOf(init) {
178
+ const call = unwrap(init);
179
+ if (call?.type !== 'CallExpression' || call.optional) return null;
180
+ const callee = unwrap(call.callee);
181
+ const identifier =
182
+ callee?.type === 'Identifier'
183
+ ? callee
184
+ : callee?.type === 'MemberExpression' && !callee.computed && !callee.optional
185
+ ? callee.object
186
+ : null;
187
+ if (identifier?.type !== 'Identifier') return null;
188
+ const record = imports.get(identifier.name);
189
+ const resolved = binding(identifier);
190
+ if (
191
+ !record ||
192
+ resolved?.scope !== lexical.rootScope ||
193
+ resolved.importSource?.value !== record.source
194
+ )
195
+ return null;
196
+ const factory =
197
+ callee === identifier
198
+ ? record.factory
199
+ : record.factory === undefined
200
+ ? callee.property.name
201
+ : null;
202
+ return factory === 'query$' || factory === 'derived$' ? record : null;
203
+ }
204
+ function strictExpression(expression) {
205
+ const call = unwrap(expression);
206
+ if (call?.type !== 'CallExpression' || call.optional || call.arguments.length !== 0)
207
+ return null;
208
+ const member = unwrap(call.callee);
209
+ if (
210
+ member?.type !== 'MemberExpression' ||
211
+ member.optional ||
212
+ member.computed ||
213
+ member.property.name !== 'get' ||
214
+ member.object.type !== 'Identifier'
215
+ )
216
+ return null;
217
+ const descriptor = recordFor(member.object);
218
+ if (descriptor?.kind !== 'const') return null;
219
+ const factory = factoryOf(descriptor.init);
220
+ return factory ? { handle: member.object, descriptor, factory } : null;
221
+ }
222
+ function strictRead(statement) {
223
+ if (
224
+ statement.type !== 'VariableDeclaration' ||
225
+ statement.kind !== 'const' ||
226
+ statement.declarations.length !== 1
227
+ )
228
+ return null;
229
+ const declaration = statement.declarations[0];
230
+ if (declaration.id.type !== 'Identifier') return null;
231
+ const read = strictExpression(declaration.init);
232
+ return read ? { ...read, result: declaration.id } : null;
233
+ }
234
+ // Only complete static native output is traversed. Mount phases can evaluate
235
+ // an attribute or typed text before an earlier generic child, so a lexical
236
+ // prefix before an opaque node (or a mix of hole kinds) is not sufficient.
237
+ function outputReads(output) {
238
+ if (isText === null) return [];
239
+ const reads = [];
240
+ let text;
241
+ function visit(node) {
242
+ if (node.type === 'JSXText') return true;
243
+ if (node.type === 'JSXExpressionContainer') {
244
+ if (node.expression.type === 'JSXEmptyExpression') return true;
245
+ const read = strictExpression(node.expression);
246
+ if (read === null) return false;
247
+ const kind = isText(node.expression);
248
+ if (text !== undefined && text !== kind) return false;
249
+ text = kind;
250
+ reads.push(read);
251
+ return true;
252
+ }
253
+ if (node.type === 'JSXElement') {
254
+ const opening = node.openingElement;
255
+ const tag = opening.name;
256
+ if (
257
+ tag.type !== 'JSXIdentifier' ||
258
+ !/^[a-z][a-z0-9]*$/.test(tag.name) ||
259
+ /^(?:html|head|body|base|meta|link|title|style|script|template|noscript|svg|math|input|select|textarea|option|optgroup|iframe|object|embed|img|image|audio|video|source|track)$/.test(
260
+ tag.name,
261
+ )
262
+ )
263
+ return false;
264
+ for (const attribute of opening.attributes ?? []) {
265
+ if (
266
+ attribute.type !== 'JSXAttribute' ||
267
+ attribute.name.type !== 'JSXIdentifier' ||
268
+ /^(?:ref|key|is|style|children|innerHTML|dangerouslySetInnerHTML|on.*)$/i.test(
269
+ attribute.name.name,
270
+ ) ||
271
+ (attribute.value !== null && attribute.value.type !== 'Literal')
272
+ )
273
+ return false;
274
+ }
275
+ } else if (node.type !== 'JSXFragment') return false;
276
+ return (node.children ?? []).every(visit);
277
+ }
278
+ return output && visit(output) ? reads : [];
279
+ }
280
+ // Follow local aliases/helpers as well as direct captures: a later selector
281
+ // closing over an earlier read's result must run after that result is assigned.
282
+ function dependsOn(node, results, seen = new Set()) {
283
+ if (!node || seen.has(node)) return false;
284
+ seen.add(node);
285
+ if (node.type === 'Identifier' && !lexical.bindingNodes.has(node)) {
286
+ const resolved = binding(node);
287
+ if (
288
+ results.some(
289
+ (result) => result.name === node.name && binding(result)?.scope === resolved?.scope,
290
+ )
291
+ )
292
+ return true;
293
+ if (dependsOn(recordFor(node)?.init, results, seen)) return true;
294
+ }
295
+ let found = false;
296
+ children(node, (child) => {
297
+ found ||= dependsOn(child, results, seen);
298
+ });
299
+ return found;
300
+ }
301
+ let helperName = '_$startSignalReads';
302
+ while (names.has(helperName)) helperName += '$';
303
+ let helperImport = null;
304
+ function start(reads, origin, primitive = false) {
305
+ helperImport ??= reads[0].factory.declaration;
306
+ return inheritHookMemoOrigin(
307
+ // Guard only speculative receiver evaluation (including module TDZ).
308
+ // The original reads retain their normal observation/error boundary.
309
+ b.try(
310
+ b.block([
311
+ b.stmt(
312
+ b.call(
313
+ b.id(helperName),
314
+ b.array(reads.map((read) => b.id(read.handle.name))),
315
+ ...(primitive ? [b.literal(true)] : []),
316
+ ),
317
+ ),
318
+ ]),
319
+ b.catch_clause(null, null, b.block([])),
320
+ ),
321
+ origin,
322
+ );
323
+ }
324
+ function statements(body) {
325
+ let out = null;
326
+ for (let i = 0; i < body.length;) {
327
+ const first = strictRead(body[i]);
328
+ if (!first) {
329
+ const reads = outputReads(body[i]);
330
+ if (reads.length > 1) {
331
+ out ??= body.slice(0, i);
332
+ out.push(start(reads, body[i], true));
333
+ }
334
+ if (out) out.push(body[i]);
335
+ i++;
336
+ continue;
337
+ }
338
+ const run = [first];
339
+ let end = i + 1;
340
+ for (; end < body.length; end++) {
341
+ const next = strictRead(body[end]);
342
+ if (
343
+ !next ||
344
+ run.some((read) => capturedBefore(read.result, body[i].start)) ||
345
+ dependsOn(
346
+ next.descriptor.init,
347
+ run.map((read) => read.result),
348
+ )
349
+ )
350
+ break;
351
+ run.push(next);
352
+ }
353
+ if (run.length > 1) {
354
+ out ??= body.slice(0, i);
355
+ out.push(start(run, body[i]));
356
+ }
357
+ if (out) out.push(...body.slice(i, end));
358
+ i = end;
359
+ }
360
+ return out ?? body;
361
+ }
362
+ function transform(node, rendering = false, functionBody = false) {
363
+ if (node === null || typeof node !== 'object') return node;
364
+ if (Array.isArray(node)) {
365
+ const mapped = node.map((child) => transform(child, rendering));
366
+ return mapped.some((child, index) => child !== node[index]) ? mapped : node;
367
+ }
368
+ if (isFunction(node)) rendering = !opaqueFunctions.has(node) && hasRender(node.body);
369
+ let out = node;
370
+ for (const key in node) {
371
+ if (METADATA.has(key) || key.startsWith('_octane')) continue;
372
+ const mapped = transform(node[key], rendering, isFunction(node) && key === 'body');
373
+ if (mapped !== node[key]) {
374
+ if (out === node) out = { ...node };
375
+ out[key] = mapped;
376
+ }
377
+ }
378
+ if (
379
+ rendering &&
380
+ (node.type === 'BlockStatement' || node.type === 'JSXCodeBlock') &&
381
+ Array.isArray(node.body)
382
+ ) {
383
+ // Analyze original nodes, whose lexical binding identities are known.
384
+ const body = statements(node.body);
385
+ if (body !== node.body) {
386
+ const mapped = new Map(node.body.map((statement, index) => [statement, out.body[index]]));
387
+ out = { ...out, body: body.map((statement) => mapped.get(statement) ?? statement) };
388
+ }
389
+ // Do not turn a transparent render-only child block into a new scope.
390
+ if (node.type === 'JSXCodeBlock' && (functionBody || node.body.length > 0)) {
391
+ const reads = outputReads(node.render);
392
+ if (reads.length > 1)
393
+ out = { ...out, body: [...out.body, start(reads, node.render, true)] };
394
+ }
395
+ }
396
+ return out;
397
+ }
398
+ const lowered = transform(ast);
399
+ if (helperImport === null) return ast;
400
+ return {
401
+ ...lowered,
402
+ body: lowered.body.flatMap((statement) => {
403
+ if (statement !== helperImport) return [statement];
404
+ const helper = inheritHookMemoOrigin(
405
+ b.import_specifier('__startSignalReads', helperName),
406
+ statement,
407
+ );
408
+ return statement.specifiers.some((specifier) => specifier.type === 'ImportNamespaceSpecifier')
409
+ ? [statement, { ...statement, specifiers: [helper] }]
410
+ : [{ ...statement, specifiers: [...statement.specifiers, helper] }];
411
+ }),
412
+ };
413
+ }
@@ -1,4 +1,4 @@
1
- import type { CompilerProgram, CompileSourceMap } from './index.js';
1
+ import type { CompilerProgram, CompileDiagnostic, CompileSourceMap } from './index.js';
2
2
 
3
3
  /** Options for the plain-module hook transform used by compiler adapters. */
4
4
  export interface SlotHooksOptions {
@@ -20,6 +20,8 @@ export interface SlotHooksResult {
20
20
  code: string;
21
21
  /** The surgical transform preserves source lines and returns no map. */
22
22
  map: CompileSourceMap | null;
23
+ /** Nonfatal authored Strong diagnostics, including redundant dependency hints. */
24
+ diagnostics?: readonly CompileDiagnostic[];
23
25
  }
24
26
 
25
27
  export interface VoidComponentImport {