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.
- package/README.md +5 -0
- package/dist/behavior-root.d.ts +13 -3
- package/dist/behavior-root.js +28 -5
- package/dist/cjs/behavior-root.cjs +32 -6
- package/dist/cjs/class-names.cjs +49 -0
- package/dist/cjs/constants.cjs +9 -0
- package/dist/cjs/context-epoch.cjs +38 -0
- package/dist/cjs/css.cjs +6 -27
- package/dist/cjs/data-encoding.cjs +160 -0
- package/dist/cjs/dom-binding-handoff.cjs +93 -0
- package/dist/cjs/dom-binding-protocol.cjs +115 -0
- package/dist/cjs/dom-binding-styles.cjs +292 -0
- package/dist/cjs/dom-bindings.cjs +700 -0
- package/dist/cjs/dom-focused-move.cjs +66 -0
- package/dist/cjs/dom-stage.cjs +896 -0
- package/dist/cjs/dom-tables.cjs +4 -94
- package/dist/cjs/error-codes.client.generated.cjs +33 -0
- package/dist/cjs/error-codes.server.generated.cjs +24 -0
- package/dist/cjs/hydration/control-capture.cjs +244 -0
- package/dist/cjs/hydration/event-capture.cjs +125 -43
- package/dist/cjs/hydration/interaction-config.cjs +50 -2
- package/dist/cjs/hydration-markers.cjs +37 -0
- package/dist/cjs/independent-hydration-protocol.cjs +106 -0
- package/dist/cjs/index.cjs +9 -0
- package/dist/cjs/runtime.cjs +6397 -1683
- package/dist/cjs/runtime.server.cjs +1465 -213
- package/dist/cjs/server/early-signals.cjs +45 -0
- package/dist/cjs/server/index.cjs +47 -0
- package/dist/cjs/server/rpc-batch.cjs +149 -0
- package/dist/cjs/server/rpc-stream.cjs +146 -0
- package/dist/cjs/server/rpc.cjs +34 -15
- package/dist/cjs/server/signal-query-observation.cjs +158 -0
- package/dist/cjs/server/streamed-region.cjs +52 -0
- package/dist/cjs/server/streamed-signals.cjs +512 -0
- package/dist/cjs/server-call.cjs +103 -0
- package/dist/cjs/server-rpc-batch-client.cjs +141 -0
- package/dist/cjs/server-rpc-client.cjs +94 -27
- package/dist/cjs/server-rpc-protocol.cjs +141 -0
- package/dist/cjs/server-rpc-stream-client.cjs +118 -0
- package/dist/cjs/signals/actions.cjs +474 -0
- package/dist/cjs/signals/client.cjs +4 -1
- package/dist/cjs/signals/computations.cjs +460 -0
- package/dist/cjs/signals/control-binding.cjs +368 -0
- package/dist/cjs/signals/derived-facade.cjs +50 -0
- package/dist/cjs/signals/document-owner.cjs +71 -0
- package/dist/cjs/signals/early-values.cjs +155 -0
- package/dist/cjs/signals/encoding.cjs +6 -95
- package/dist/cjs/signals/engine.cjs +276 -12
- package/dist/cjs/signals/errors.cjs +18 -0
- package/dist/cjs/signals/facade.cjs +477 -0
- package/dist/cjs/signals/graph.cjs +166 -20
- package/dist/cjs/signals/handle-protocol.cjs +36 -0
- package/dist/cjs/signals/index.cjs +81 -2
- package/dist/cjs/signals/native-read-client.cjs +10 -0
- package/dist/cjs/signals/owner-context.cjs +112 -0
- package/dist/cjs/signals/query-attempt-observer.cjs +68 -0
- package/dist/cjs/signals/query-facade.cjs +72 -0
- package/dist/cjs/signals/read-protocol.cjs +54 -0
- package/dist/cjs/signals/requests.cjs +287 -14
- package/dist/cjs/signals/scalar-computations.cjs +76 -0
- package/dist/cjs/signals/server.cjs +4 -1
- package/dist/cjs/signals/transition-candidate.cjs +567 -0
- package/dist/cjs/signals/transition-state.cjs +106 -0
- package/dist/cjs/signals/types.cjs +50 -0
- package/dist/cjs/stream-protocol.cjs +3 -0
- package/dist/cjs/streamed-signals-protocol.cjs +159 -0
- package/dist/cjs/style-values.cjs +122 -0
- package/dist/cjs/trusted-html.cjs +30 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/class-names.d.ts +5 -0
- package/dist/class-names.js +25 -0
- package/dist/compiler/bundler.js +51 -22
- package/dist/compiler/compile.js +2534 -581
- package/dist/compiler/dom-binding-program.js +935 -0
- package/dist/compiler/dom-binding-request.js +57 -0
- package/dist/compiler/dom-bindings.js +2245 -0
- package/dist/compiler/hook-deps.js +759 -10
- package/dist/compiler/hydrate-boundaries.js +358 -15
- package/dist/compiler/index.d.ts +39 -2
- package/dist/compiler/index.js +1 -0
- package/dist/compiler/native-attribute-reads.js +127 -0
- package/dist/compiler/native-change-diagnostics.js +32 -38
- package/dist/compiler/native-read-diagnostics.js +69 -8
- package/dist/compiler/native-read-types.js +80 -34
- package/dist/compiler/parser.node.js +1 -1
- package/dist/compiler/renderer-boundaries.js +40 -0
- package/dist/compiler/server-context.js +118 -0
- package/dist/compiler/signal-attempt-reads.js +479 -0
- package/dist/compiler/signal-declarations.js +557 -0
- package/dist/compiler/signal-start-reads.js +413 -0
- package/dist/compiler/slot-hooks.d.ts +3 -1
- package/dist/compiler/slot-hooks.js +168 -34
- package/dist/compiler/strong-auto-memo.js +90 -0
- package/dist/compiler/strong-html.js +143 -0
- package/dist/compiler/strong-mode.js +764 -66
- package/dist/compiler/strong-template-policy.js +307 -0
- package/dist/compiler/template-ir.js +200 -0
- package/dist/compiler/typescript.d.ts +2 -0
- package/dist/compiler/typescript.js +2 -0
- package/dist/compiler/vite.d.ts +3 -1
- package/dist/compiler/vite.js +122 -4
- package/dist/compiler/volar.LICENSES.txt +1 -1
- package/dist/compiler/volar.d.ts +6 -1
- package/dist/compiler/volar.js +750 -1433
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +10 -0
- package/dist/context-epoch.d.ts +2 -0
- package/dist/context-epoch.js +13 -0
- package/dist/css.d.ts +2 -5
- package/dist/css.js +2 -23
- package/dist/data-encoding.d.ts +9 -0
- package/dist/data-encoding.js +134 -0
- package/dist/dom-binding-classes.d.ts +15 -0
- package/dist/dom-binding-classes.js +129 -0
- package/dist/dom-binding-controls.d.ts +2 -0
- package/dist/dom-binding-controls.js +6 -0
- package/dist/dom-binding-handoff.d.ts +55 -0
- package/dist/dom-binding-handoff.js +62 -0
- package/dist/dom-binding-program.d.ts +300 -0
- package/dist/dom-binding-program.js +1625 -0
- package/dist/dom-binding-projections.d.ts +15 -0
- package/dist/dom-binding-projections.js +187 -0
- package/dist/dom-binding-protocol.d.ts +13 -0
- package/dist/dom-binding-protocol.js +88 -0
- package/dist/dom-binding-signals.d.ts +21 -0
- package/dist/dom-binding-signals.js +109 -0
- package/dist/dom-binding-styles.d.ts +17 -0
- package/dist/dom-binding-styles.js +272 -0
- package/dist/dom-bindings.d.ts +89 -0
- package/dist/dom-bindings.js +670 -0
- package/dist/dom-focused-move.d.ts +6 -0
- package/dist/dom-focused-move.js +42 -0
- package/dist/dom-stage.d.ts +52 -0
- package/dist/dom-stage.js +872 -0
- package/dist/dom-tables.d.ts +1 -3
- package/dist/dom-tables.js +1 -91
- package/dist/error-codes.client.generated.d.ts +9 -0
- package/dist/error-codes.client.generated.js +33 -0
- package/dist/error-codes.server.generated.d.ts +6 -0
- package/dist/error-codes.server.generated.js +24 -0
- package/dist/hydration/control-capture.d.ts +62 -0
- package/dist/hydration/control-capture.js +223 -0
- package/dist/hydration/document-lifecycle.d.ts +23 -0
- package/dist/hydration/document-lifecycle.js +80 -0
- package/dist/hydration/event-capture.d.ts +20 -1
- package/dist/hydration/event-capture.js +128 -42
- package/dist/hydration/independent-island.d.ts +45 -0
- package/dist/hydration/independent-island.js +233 -0
- package/dist/hydration/index.d.ts +8 -1
- package/dist/hydration/index.js +55 -1
- package/dist/hydration/interaction-config.d.ts +5 -0
- package/dist/hydration/interaction-config.js +44 -1
- package/dist/hydration/stream-delivery.d.ts +32 -0
- package/dist/hydration/stream-delivery.js +333 -0
- package/dist/hydration/stream-receiver.d.ts +26 -0
- package/dist/hydration/stream-receiver.js +204 -0
- package/dist/hydration/stream-result-receiver.d.ts +70 -0
- package/dist/hydration/stream-result-receiver.js +246 -0
- package/dist/hydration/streamed-signals.d.ts +44 -0
- package/dist/hydration/streamed-signals.js +186 -0
- package/dist/hydration/types.d.ts +6 -0
- package/dist/hydration-markers.d.ts +7 -0
- package/dist/hydration-markers.js +10 -0
- package/dist/independent-hydration-protocol.d.ts +31 -0
- package/dist/independent-hydration-protocol.js +80 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +6 -0
- package/dist/internal/signal-read.d.ts +1 -0
- package/dist/internal/signal-read.js +4 -0
- package/dist/jsx-runtime-strong.d.ts +33 -0
- package/dist/jsx-runtime.d.ts +58 -2
- package/dist/public-types.d.ts +15 -1
- package/dist/runtime.d.ts +297 -23
- package/dist/runtime.js +6405 -1679
- package/dist/runtime.server.d.ts +110 -27
- package/dist/runtime.server.js +1458 -213
- package/dist/server/early-signals.d.ts +14 -0
- package/dist/server/early-signals.js +34 -0
- package/dist/server/index.d.ts +11 -2
- package/dist/server/index.js +50 -1
- package/dist/server/rpc-batch.d.ts +17 -0
- package/dist/server/rpc-batch.js +129 -0
- package/dist/server/rpc-stream.d.ts +9 -0
- package/dist/server/rpc-stream.js +125 -0
- package/dist/server/rpc.d.ts +5 -1
- package/dist/server/rpc.js +37 -13
- package/dist/server/signal-query-observation.d.ts +3 -0
- package/dist/server/signal-query-observation.js +134 -0
- package/dist/server/streamed-region.d.ts +15 -0
- package/dist/server/streamed-region.js +30 -0
- package/dist/server/streamed-signals.d.ts +45 -0
- package/dist/server/streamed-signals.js +488 -0
- package/dist/server-call.d.ts +41 -0
- package/dist/server-call.js +75 -0
- package/dist/server-rpc-batch-client.d.ts +7 -0
- package/dist/server-rpc-batch-client.js +119 -0
- package/dist/server-rpc-client.d.ts +3 -1
- package/dist/server-rpc-client.js +99 -27
- package/dist/server-rpc-protocol.d.ts +47 -0
- package/dist/server-rpc-protocol.js +109 -0
- package/dist/server-rpc-stream-client.d.ts +5 -0
- package/dist/server-rpc-stream-client.js +98 -0
- package/dist/signals/actions.d.ts +12 -0
- package/dist/signals/actions.js +466 -0
- package/dist/signals/client.d.ts +2 -0
- package/dist/signals/client.js +1 -0
- package/dist/signals/computations.d.ts +37 -0
- package/dist/signals/computations.js +455 -0
- package/dist/signals/control-binding.d.ts +31 -0
- package/dist/signals/control-binding.js +353 -0
- package/dist/signals/derived-facade.d.ts +3 -0
- package/dist/signals/derived-facade.js +25 -0
- package/dist/signals/document-owner.d.ts +10 -0
- package/dist/signals/document-owner.js +43 -0
- package/dist/signals/early-values.d.ts +24 -0
- package/dist/signals/early-values.js +124 -0
- package/dist/signals/encoding.d.ts +1 -3
- package/dist/signals/encoding.js +3 -93
- package/dist/signals/engine.d.ts +53 -4
- package/dist/signals/engine.js +275 -16
- package/dist/signals/errors.d.ts +7 -0
- package/dist/signals/errors.js +16 -0
- package/dist/signals/facade.d.ts +74 -0
- package/dist/signals/facade.js +452 -0
- package/dist/signals/graph.d.ts +52 -6
- package/dist/signals/graph.js +174 -19
- package/dist/signals/handle-protocol.d.ts +4 -0
- package/dist/signals/handle-protocol.js +11 -0
- package/dist/signals/index.d.ts +12 -3
- package/dist/signals/index.js +71 -2
- package/dist/signals/native-read-client.d.ts +3 -0
- package/dist/signals/native-read-client.js +10 -0
- package/dist/signals/owner-context.d.ts +22 -0
- package/dist/signals/owner-context.js +77 -0
- package/dist/signals/query-attempt-observer.d.ts +49 -0
- package/dist/signals/query-attempt-observer.js +41 -0
- package/dist/signals/query-facade.d.ts +3 -0
- package/dist/signals/query-facade.js +49 -0
- package/dist/signals/read-protocol.d.ts +37 -2
- package/dist/signals/read-protocol.js +44 -0
- package/dist/signals/requests.d.ts +38 -5
- package/dist/signals/requests.js +298 -15
- package/dist/signals/scalar-computations.d.ts +2 -0
- package/dist/signals/scalar-computations.js +60 -0
- package/dist/signals/server.d.ts +2 -0
- package/dist/signals/server.js +1 -0
- package/dist/signals/transition-candidate.d.ts +71 -0
- package/dist/signals/transition-candidate.js +557 -0
- package/dist/signals/transition-state.d.ts +51 -0
- package/dist/signals/transition-state.js +70 -0
- package/dist/signals/types.d.ts +95 -5
- package/dist/signals/types.js +20 -0
- package/dist/stream-protocol.d.ts +0 -5
- package/dist/stream-protocol.js +3 -0
- package/dist/streamed-signals-protocol.d.ts +60 -0
- package/dist/streamed-signals-protocol.js +130 -0
- package/dist/style-values.d.ts +3 -0
- package/dist/style-values.js +96 -0
- package/dist/trusted-html.d.ts +17 -0
- package/dist/trusted-html.js +6 -0
- package/dist/universal-core.d.ts +6 -0
- package/dist/universal-core.js +214 -202
- package/dist/version.js +1 -1
- package/package.json +48 -5
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Direct native bindings recognize capability syntax, not structural `.get`
|
|
2
|
+
// methods. Share this syntax boundary between runtime and virtual TSX lowering.
|
|
3
|
+
function unwrapExpression(node) {
|
|
4
|
+
while (
|
|
5
|
+
node &&
|
|
6
|
+
(node.type === 'TSAsExpression' ||
|
|
7
|
+
node.type === 'TSTypeAssertion' ||
|
|
8
|
+
node.type === 'TSNonNullExpression' ||
|
|
9
|
+
node.type === 'TSSatisfiesExpression' ||
|
|
10
|
+
node.type === 'TSInstantiationExpression' ||
|
|
11
|
+
node.type === 'ChainExpression' ||
|
|
12
|
+
node.type === 'ParenthesizedExpression')
|
|
13
|
+
) {
|
|
14
|
+
node = node.expression;
|
|
15
|
+
}
|
|
16
|
+
return node;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isDirectSignalHandleExpression(node) {
|
|
20
|
+
node = unwrapExpression(node);
|
|
21
|
+
if (!node) return false;
|
|
22
|
+
if (node.type === 'Identifier') return node.name.endsWith('$');
|
|
23
|
+
if (node.type === 'MemberExpression' || node.type === 'OptionalMemberExpression') {
|
|
24
|
+
const name = node.computed
|
|
25
|
+
? node.property?.type === 'Literal' && typeof node.property.value === 'string'
|
|
26
|
+
? node.property.value
|
|
27
|
+
: null
|
|
28
|
+
: node.property?.name;
|
|
29
|
+
return typeof name === 'string' && name.endsWith('$');
|
|
30
|
+
}
|
|
31
|
+
if (node.type === 'CallExpression' || node.type === 'OptionalCallExpression') {
|
|
32
|
+
const callee = unwrapExpression(node.callee);
|
|
33
|
+
const name =
|
|
34
|
+
callee?.type === 'Identifier'
|
|
35
|
+
? callee.name
|
|
36
|
+
: callee?.type === 'MemberExpression' || callee?.type === 'OptionalMemberExpression'
|
|
37
|
+
? callee.computed
|
|
38
|
+
? callee.property?.value
|
|
39
|
+
: callee.property?.name
|
|
40
|
+
: null;
|
|
41
|
+
return /^_?\$?__(?:signal|derived|query)At$/.test(name ?? '');
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function rejectNestedHandles(node) {
|
|
47
|
+
if (!node || typeof node !== 'object') return;
|
|
48
|
+
if (isDirectSignalHandleExpression(node))
|
|
49
|
+
throw new Error(
|
|
50
|
+
'Native JSX signal projections do not support handles inside callbacks, constructors, or tagged templates.',
|
|
51
|
+
);
|
|
52
|
+
for (const key of Object.keys(node)) {
|
|
53
|
+
if (!['metadata', 'loc', 'start', 'end'].includes(key)) rejectNestedHandles(node[key]);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Only an explicitly configured native attribute admits these sampled reads. */
|
|
58
|
+
export function lowerNativeAttributeReads(expression, read) {
|
|
59
|
+
let reactive = false;
|
|
60
|
+
const rewrite = (node) => {
|
|
61
|
+
if (!node || typeof node !== 'object') return node;
|
|
62
|
+
if (Array.isArray(node)) return node.map(rewrite);
|
|
63
|
+
if (isDirectSignalHandleExpression(node)) {
|
|
64
|
+
reactive = true;
|
|
65
|
+
return read(node);
|
|
66
|
+
}
|
|
67
|
+
if (node.type === 'MemberExpression' || node.type === 'OptionalMemberExpression') {
|
|
68
|
+
if (isDirectSignalHandleExpression(node.object))
|
|
69
|
+
throw new Error(
|
|
70
|
+
'Native JSX signal projections require .get() before reading a signal payload property.',
|
|
71
|
+
);
|
|
72
|
+
return {
|
|
73
|
+
...node,
|
|
74
|
+
object: rewrite(node.object),
|
|
75
|
+
property: node.computed ? rewrite(node.property) : node.property,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (node.type === 'CallExpression' || node.type === 'OptionalCallExpression') {
|
|
79
|
+
const callee = unwrapExpression(node.callee);
|
|
80
|
+
const handleGet =
|
|
81
|
+
(callee?.type === 'MemberExpression' || callee?.type === 'OptionalMemberExpression') &&
|
|
82
|
+
(callee.computed ? callee.property?.value : callee.property?.name) === 'get' &&
|
|
83
|
+
isDirectSignalHandleExpression(callee.object);
|
|
84
|
+
if (handleGet) reactive = true;
|
|
85
|
+
return {
|
|
86
|
+
...node,
|
|
87
|
+
// Keep a method call as a member call so `this` and evaluation order
|
|
88
|
+
// survive a computed style selection. Explicit get keeps its receiver.
|
|
89
|
+
callee:
|
|
90
|
+
!handleGet &&
|
|
91
|
+
(callee?.type === 'MemberExpression' || callee?.type === 'OptionalMemberExpression')
|
|
92
|
+
? rewrite(node.callee)
|
|
93
|
+
: node.callee,
|
|
94
|
+
arguments: node.arguments.map(rewrite),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (
|
|
98
|
+
[
|
|
99
|
+
'FunctionExpression',
|
|
100
|
+
'ArrowFunctionExpression',
|
|
101
|
+
'ClassExpression',
|
|
102
|
+
'NewExpression',
|
|
103
|
+
'TaggedTemplateExpression',
|
|
104
|
+
].includes(node.type)
|
|
105
|
+
) {
|
|
106
|
+
rejectNestedHandles(node);
|
|
107
|
+
return node;
|
|
108
|
+
}
|
|
109
|
+
if (node.type === 'Property') {
|
|
110
|
+
const value = rewrite(node.value);
|
|
111
|
+
return value === node.value ? node : { ...node, value, shorthand: false };
|
|
112
|
+
}
|
|
113
|
+
let result = node;
|
|
114
|
+
for (const key of Object.keys(node)) {
|
|
115
|
+
if (['metadata', 'loc', 'start', 'end'].includes(key)) continue;
|
|
116
|
+
const value = node[key];
|
|
117
|
+
if (!value || typeof value !== 'object') continue;
|
|
118
|
+
const next = rewrite(value);
|
|
119
|
+
if (next !== value) {
|
|
120
|
+
if (result === node) result = { ...node };
|
|
121
|
+
result[key] = next;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return result;
|
|
125
|
+
};
|
|
126
|
+
return { expression: rewrite(expression), reactive };
|
|
127
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { builders as b } from '@tsrx/core';
|
|
2
2
|
import { nsForChildren, nsForSelf } from './jsx-namespace.js';
|
|
3
|
-
import {
|
|
3
|
+
import { createRendererRegionResolver } from './renderer-boundaries.js';
|
|
4
4
|
|
|
5
5
|
export const NATIVE_TEXT_ONCHANGE_DIAGNOSTIC = 'OCTANE_NATIVE_TEXT_ONCHANGE';
|
|
6
6
|
|
|
@@ -263,15 +263,16 @@ function hostLabel(tag, type) {
|
|
|
263
263
|
return tag === 'textarea' ? '<textarea>' : `<input type="${type.display}">`;
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
function diagnosticFor(source, filename, tag, type, changeAttributes, controlled) {
|
|
266
|
+
function diagnosticFor(source, filename, tag, type, changeAttributes, controlled, strong) {
|
|
267
267
|
const first = changeAttributes[0];
|
|
268
268
|
const captureOnly = attributeName(first) === 'onChangeCapture';
|
|
269
269
|
const replacement = captureOnly ? 'onInputCapture' : 'onInput';
|
|
270
270
|
let message =
|
|
271
271
|
`[${NATIVE_TEXT_ONCHANGE_DIAGNOSTIC}] \`${attributeName(first)}\` on ${hostLabel(tag, type)} ` +
|
|
272
272
|
`is a native commit event in Octane; it does not run for each text edit. Use \`${replacement}\` ` +
|
|
273
|
-
|
|
274
|
-
|
|
273
|
+
(strong
|
|
274
|
+
? 'for per-edit updates. Strong mode requires an explicit per-edit handler for editable text controls.'
|
|
275
|
+
: 'for per-edit updates. If commit/blur behavior is intentional, add `suppressNativeChangeWarning`.');
|
|
275
276
|
if (controlled) {
|
|
276
277
|
message +=
|
|
277
278
|
' This control also has `value`; edits are restored before the later native change. ' +
|
|
@@ -280,7 +281,7 @@ function diagnosticFor(source, filename, tag, type, changeAttributes, controlled
|
|
|
280
281
|
const primary = rangeFor(source, first.name ?? first);
|
|
281
282
|
return {
|
|
282
283
|
code: NATIVE_TEXT_ONCHANGE_DIAGNOSTIC,
|
|
283
|
-
severity: 'warning',
|
|
284
|
+
severity: strong ? 'error' : 'warning',
|
|
284
285
|
message,
|
|
285
286
|
filename: filename || 'module.tsrx',
|
|
286
287
|
start: primary.start,
|
|
@@ -295,7 +296,16 @@ function diagnosticFor(source, filename, tag, type, changeAttributes, controlled
|
|
|
295
296
|
};
|
|
296
297
|
}
|
|
297
298
|
|
|
298
|
-
function classifyHost(
|
|
299
|
+
function classifyHost(
|
|
300
|
+
node,
|
|
301
|
+
scope,
|
|
302
|
+
namespace,
|
|
303
|
+
source,
|
|
304
|
+
filename,
|
|
305
|
+
diagnostics,
|
|
306
|
+
classifications,
|
|
307
|
+
strong,
|
|
308
|
+
) {
|
|
299
309
|
const tag = tagName(node);
|
|
300
310
|
if ((tag !== 'input' && tag !== 'textarea') || namespace !== 'html') return;
|
|
301
311
|
const attributes = attributesOf(node);
|
|
@@ -365,6 +375,7 @@ function classifyHost(node, scope, namespace, source, filename, diagnostics, cla
|
|
|
365
375
|
type ?? { kind: 'text', display: 'text' },
|
|
366
376
|
changeAttributes,
|
|
367
377
|
lastAttribute(attributes, 'value') !== null,
|
|
378
|
+
strong,
|
|
368
379
|
),
|
|
369
380
|
);
|
|
370
381
|
}
|
|
@@ -377,41 +388,15 @@ function classifyHost(node, scope, namespace, source, filename, diagnostics, cla
|
|
|
377
388
|
export function analyzeNativeChangeDiagnostics(ast, source, filename, options = {}) {
|
|
378
389
|
// The parser permits trivia after `<` and Unicode escapes in JSX names. A
|
|
379
390
|
// decoded lowercase input/textarea must still leave either its literal name
|
|
380
|
-
// or a
|
|
391
|
+
// or a Unicode escape in the paired authored source. False positives only retain
|
|
381
392
|
// the existing analysis for unrelated identifiers, comments, and strings.
|
|
382
|
-
if (!source.includes('input') && !source.includes('textarea') && !source.includes('\\')) {
|
|
393
|
+
if (!source.includes('input') && !source.includes('textarea') && !source.includes('\\u')) {
|
|
383
394
|
return { diagnostics: [], classifications: new Map() };
|
|
384
395
|
}
|
|
385
396
|
const diagnostics = [];
|
|
386
397
|
const classifications = new Map();
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
if (options.rendererBoundaries && Object.keys(options.rendererBoundaries).length > 0) {
|
|
390
|
-
rendererRegions = analyzeRendererBoundaries(source, {
|
|
391
|
-
filename,
|
|
392
|
-
rendererBoundaries: options.rendererBoundaries,
|
|
393
|
-
})
|
|
394
|
-
.boundaries.map((boundary) => ({
|
|
395
|
-
childRenderer: boundary.childRenderer,
|
|
396
|
-
range: boundary.region?.range ?? boundary.region?.valueRange,
|
|
397
|
-
}))
|
|
398
|
-
.filter((region) => region.range !== null);
|
|
399
|
-
}
|
|
400
|
-
const rendererIsDomAt = (node) => {
|
|
401
|
-
let rendererId = ownerRendererId;
|
|
402
|
-
let narrowest = Number.POSITIVE_INFINITY;
|
|
403
|
-
for (const region of rendererRegions) {
|
|
404
|
-
const [start, end] = region.range;
|
|
405
|
-
if (start > node.start || node.end > end) continue;
|
|
406
|
-
const width = end - start;
|
|
407
|
-
if (width < narrowest) {
|
|
408
|
-
rendererId = region.childRenderer;
|
|
409
|
-
narrowest = width;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
if (rendererId === 'dom') return true;
|
|
413
|
-
return options.rendererRegistry?.[rendererId]?.target === 'dom';
|
|
414
|
-
};
|
|
398
|
+
const rendererIsDomAt =
|
|
399
|
+
options.rendererRegionResolver ?? createRendererRegionResolver(ast, source, filename, options);
|
|
415
400
|
|
|
416
401
|
const programScope = createScope(null, ast?.body ?? []);
|
|
417
402
|
const seen = new WeakSet();
|
|
@@ -484,7 +469,16 @@ export function analyzeNativeChangeDiagnostics(ast, source, filename, options =
|
|
|
484
469
|
const isHost = typeof tag === 'string' && /^[a-z]/.test(tag);
|
|
485
470
|
const selfNs = isHost ? nsForSelf(tag, parentNs) : parentNs;
|
|
486
471
|
if (isHost && rendererIsDomAt(node)) {
|
|
487
|
-
classifyHost(
|
|
472
|
+
classifyHost(
|
|
473
|
+
node,
|
|
474
|
+
scope,
|
|
475
|
+
selfNs,
|
|
476
|
+
source,
|
|
477
|
+
filename,
|
|
478
|
+
diagnostics,
|
|
479
|
+
classifications,
|
|
480
|
+
options.strong === true,
|
|
481
|
+
);
|
|
488
482
|
}
|
|
489
483
|
|
|
490
484
|
for (const attribute of attributesOf(node)) {
|
|
@@ -508,5 +502,5 @@ export function analyzeNativeChangeDiagnostics(ast, source, filename, options =
|
|
|
508
502
|
}
|
|
509
503
|
|
|
510
504
|
export function formatCompileDiagnostic(diagnostic) {
|
|
511
|
-
return `${diagnostic.filename}:${diagnostic.start.line}:${diagnostic.start.column + 1} ${diagnostic.message}`;
|
|
505
|
+
return `${diagnostic.filename}:${diagnostic.start.line}:${diagnostic.start.column + 1} ${diagnostic.severity}: ${diagnostic.message}`;
|
|
512
506
|
}
|
|
@@ -5,9 +5,11 @@ export { NATIVE_SIGNAL_NAME, NATIVE_MEMO_READ } from './native-read-facts.js';
|
|
|
5
5
|
|
|
6
6
|
const SIGNALS_MODULE = 'octane/signals';
|
|
7
7
|
const CLIENT_MODULE = 'octane/signals/client';
|
|
8
|
+
const SIGNAL_MODULES = new Set([SIGNALS_MODULE, CLIENT_MODULE, 'octane/signals/server']);
|
|
8
9
|
const LOCAL_MODULES = new Set([CLIENT_MODULE, 'octane/signals/server']);
|
|
9
10
|
const HANDLE_TYPES = new Set(['SignalHandle', 'Resource', 'WritableSignal', 'DerivedSignal']);
|
|
10
|
-
const SIGNAL_METHODS = new Set(['signal$', 'derived$'
|
|
11
|
+
const SIGNAL_METHODS = new Set(['signal$', 'derived$']);
|
|
12
|
+
const SIGNAL_FACTORIES = new Set(['signal$', 'derived$', 'query$', 'createResource']);
|
|
11
13
|
const LOCAL_HOOKS = new Set(['useSignal$']);
|
|
12
14
|
const FUNCTION_TYPES = new Set([
|
|
13
15
|
'ArrowFunctionExpression',
|
|
@@ -189,8 +191,12 @@ export function analyzeNativeReadDiagnostics(ast, source, filename, options = {}
|
|
|
189
191
|
}
|
|
190
192
|
}
|
|
191
193
|
function importedValue(module, name) {
|
|
194
|
+
if (module === 'octane/behavior' && (name === 'adoptBindings' || name === 'mountBindings'))
|
|
195
|
+
return { kind: 'builtin', name, bindingSource: true };
|
|
192
196
|
if (module === SIGNALS_MODULE && name === 'createScope')
|
|
193
197
|
return { kind: 'builtin', name: 'createScope' };
|
|
198
|
+
if (SIGNAL_MODULES.has(module) && SIGNAL_FACTORIES.has(name))
|
|
199
|
+
return { kind: 'builtin', name, capability: true };
|
|
194
200
|
if (LOCAL_MODULES.has(module) && name === 'useDerived$')
|
|
195
201
|
return { kind: 'unsupportedHook', name };
|
|
196
202
|
if (LOCAL_MODULES.has(module) && LOCAL_HOOKS.has(name))
|
|
@@ -382,7 +388,12 @@ export function analyzeNativeReadDiagnostics(ast, source, filename, options = {}
|
|
|
382
388
|
if (callee.kind === 'function') return callee.result;
|
|
383
389
|
if (callee.kind === 'builtin') {
|
|
384
390
|
if (callee.name === 'createScope') return SCOPE;
|
|
385
|
-
if (
|
|
391
|
+
if (
|
|
392
|
+
SIGNAL_METHODS.has(callee.name) ||
|
|
393
|
+
SIGNAL_FACTORIES.has(callee.name) ||
|
|
394
|
+
LOCAL_HOOKS.has(callee.name)
|
|
395
|
+
)
|
|
396
|
+
return HANDLE;
|
|
386
397
|
}
|
|
387
398
|
return UNKNOWN;
|
|
388
399
|
}
|
|
@@ -464,6 +475,16 @@ export function analyzeNativeReadDiagnostics(ast, source, filename, options = {}
|
|
|
464
475
|
}
|
|
465
476
|
}
|
|
466
477
|
} while (changed);
|
|
478
|
+
// BindingSource uses a fixed protocol key even when its snapshot carries
|
|
479
|
+
// native handles. Exempt only callbacks reached through the source argument
|
|
480
|
+
// of a directly imported bindings API, never arbitrary getSnapshot methods.
|
|
481
|
+
const bindingSnapshots = new WeakSet();
|
|
482
|
+
for (const node of nodes) {
|
|
483
|
+
if (node.type !== 'CallExpression' && node.type !== 'OptionalCallExpression') continue;
|
|
484
|
+
if (recordFor(unwrap(node.callee))?.forced?.bindingSource !== true) continue;
|
|
485
|
+
const snapshot = memberValue(valueOf(node.arguments[2]), 'getSnapshot');
|
|
486
|
+
if (snapshot.kind === 'function') bindingSnapshots.add(snapshot.fn);
|
|
487
|
+
}
|
|
467
488
|
function containsHandle(value, active = new Set()) {
|
|
468
489
|
if (value.kind === 'handle') return true;
|
|
469
490
|
if (value.kind !== 'object' || active.has(value)) return false;
|
|
@@ -510,9 +531,45 @@ export function analyzeNativeReadDiagnostics(ast, source, filename, options = {}
|
|
|
510
531
|
`Native signal handles and functions exposing handles or live reads must end in $. Rename ${JSON.stringify(name)} to ${JSON.stringify(name + '$')}; sampled values keep ordinary names.`,
|
|
511
532
|
);
|
|
512
533
|
}
|
|
534
|
+
function createsCapability(node, value) {
|
|
535
|
+
node = unwrap(node);
|
|
536
|
+
if (!node || !isCapability(value)) return false;
|
|
537
|
+
return (
|
|
538
|
+
node.type === 'CallExpression' ||
|
|
539
|
+
node.type === 'OptionalCallExpression' ||
|
|
540
|
+
FUNCTION_TYPES.has(node.type)
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
function isDomStyleProperty(node) {
|
|
544
|
+
let object = parents.get(node);
|
|
545
|
+
let container = parents.get(object);
|
|
546
|
+
while (
|
|
547
|
+
container &&
|
|
548
|
+
(WRAPPERS.has(container.type) ||
|
|
549
|
+
(container.type === 'LogicalExpression' &&
|
|
550
|
+
(container.operator !== '&&' || container.right === object)) ||
|
|
551
|
+
(container.type === 'ConditionalExpression' &&
|
|
552
|
+
(container.consequent === object || container.alternate === object)))
|
|
553
|
+
) {
|
|
554
|
+
object = container;
|
|
555
|
+
container = parents.get(object);
|
|
556
|
+
}
|
|
557
|
+
if (container?.type !== 'JSXExpressionContainer') return false;
|
|
558
|
+
const attribute = parents.get(container);
|
|
559
|
+
if (attribute?.type !== 'JSXAttribute' || attribute.name?.name !== 'style') return false;
|
|
560
|
+
const opening = parents.get(attribute);
|
|
561
|
+
const tag = opening?.name;
|
|
562
|
+
return tag?.type === 'JSXIdentifier' && /^[a-z]/.test(tag.name);
|
|
563
|
+
}
|
|
513
564
|
for (const record of allRecords) {
|
|
514
565
|
const value = recordValue(record);
|
|
515
|
-
|
|
566
|
+
if (
|
|
567
|
+
record.expressions.some(({ expression }) =>
|
|
568
|
+
createsCapability(expression, valueOf(expression)),
|
|
569
|
+
)
|
|
570
|
+
) {
|
|
571
|
+
for (const declaration of record.declarations) checkName(declaration, record.name, value);
|
|
572
|
+
}
|
|
516
573
|
}
|
|
517
574
|
for (const node of nodes) {
|
|
518
575
|
if (
|
|
@@ -526,13 +583,17 @@ export function analyzeNativeReadDiagnostics(ast, source, filename, options = {}
|
|
|
526
583
|
'useDerived$ is not available. Create derived$ on an explicitly owned Scope and pass the handle to the component.',
|
|
527
584
|
);
|
|
528
585
|
} else if (node.type === 'Property' && parents.get(node)?.type === 'ObjectExpression') {
|
|
529
|
-
|
|
586
|
+
const value = valueOf(node.value);
|
|
587
|
+
const bindingSnapshot =
|
|
588
|
+
propertyName(node.key, node.computed) === 'getSnapshot' &&
|
|
589
|
+
bindingSnapshots.has(unwrap(node.value));
|
|
590
|
+
if (!isDomStyleProperty(node) && !bindingSnapshot && createsCapability(node.value, value))
|
|
591
|
+
checkName(node.key, propertyName(node.key, node.computed), value);
|
|
530
592
|
} else if (node.type === 'AssignmentExpression' && node.operator === '=') {
|
|
531
593
|
const left = unwrap(node.left);
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
checkName(node.exported, node.exported.name ?? node.exported.value, valueOf(node.local));
|
|
594
|
+
const value = valueOf(node.right);
|
|
595
|
+
if (left?.type === 'MemberExpression' && createsCapability(node.right, value))
|
|
596
|
+
checkName(left.property, propertyName(left.property, left.computed), value);
|
|
536
597
|
} else if (node.type === 'CallExpression' || node.type === 'OptionalCallExpression') {
|
|
537
598
|
const callee = valueOf(node.callee);
|
|
538
599
|
if (callee.kind === 'builtin' && callee.name === 'useMemo') {
|
|
@@ -57,6 +57,22 @@ export function validateNativeSignalNames(program, file) {
|
|
|
57
57
|
if (property) recordSignatures(symbolType(property), nativeReadSignatures);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
+
function recordFactory(exports, name) {
|
|
61
|
+
const factory = exports.get(name);
|
|
62
|
+
const type = factory && symbolType(factory);
|
|
63
|
+
if (!type) return;
|
|
64
|
+
for (const signature of checker.getSignaturesOfType(type, ts.SignatureKind.Call)) {
|
|
65
|
+
const returned = checker.getReturnTypeOfSignature(signature);
|
|
66
|
+
recordReads(returned, ['get', 'latest', 'snapshot']);
|
|
67
|
+
for (const property of checker.getPropertiesOfType(returned)) {
|
|
68
|
+
for (const declaration of property.declarations ?? []) {
|
|
69
|
+
if (!declaration.name || !ts.isComputedPropertyName(declaration.name)) continue;
|
|
70
|
+
const marker = canonical(checker.getSymbolAtLocation(declaration.name.expression));
|
|
71
|
+
if (marker?.name === 'SIGNAL_HANDLE') brands.add(marker);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
60
76
|
function inspectModule(symbol, request) {
|
|
61
77
|
symbol = canonical(symbol);
|
|
62
78
|
if (!symbol || inspectedModules.has(symbol)) return;
|
|
@@ -77,24 +93,14 @@ export function validateNativeSignalNames(program, file) {
|
|
|
77
93
|
if (type)
|
|
78
94
|
for (const signature of checker.getSignaturesOfType(type, ts.SignatureKind.Call))
|
|
79
95
|
recordReads(checker.getReturnTypeOfSignature(signature), ['get']);
|
|
96
|
+
for (const name of ['signal$', 'derived$', 'query$']) recordFactory(exports, name);
|
|
80
97
|
} else if (NATIVE_MODULES.has(request)) {
|
|
81
98
|
// A project may import only the optional local hook entry. Follow the
|
|
82
99
|
// trusted export's actual return type to the same nominal declaration;
|
|
83
100
|
// do not require a redundant bare-engine import or brand lookalikes.
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
for (const signature of checker.getSignaturesOfType(type, ts.SignatureKind.Call)) {
|
|
88
|
-
const returned = checker.getReturnTypeOfSignature(signature);
|
|
89
|
-
recordReads(returned, ['get', 'latest', 'snapshot']);
|
|
90
|
-
for (const property of checker.getPropertiesOfType(returned)) {
|
|
91
|
-
for (const declaration of property.declarations ?? []) {
|
|
92
|
-
if (!declaration.name || !ts.isComputedPropertyName(declaration.name)) continue;
|
|
93
|
-
const marker = canonical(checker.getSymbolAtLocation(declaration.name.expression));
|
|
94
|
-
if (marker?.name === 'SIGNAL_HANDLE') brands.add(marker);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
101
|
+
for (const name of ['useSignal$', 'signal$', 'derived$', 'query$']) {
|
|
102
|
+
recordFactory(exports, name);
|
|
103
|
+
}
|
|
98
104
|
} else if (request === 'octane') {
|
|
99
105
|
const memo = exports.get('useMemo');
|
|
100
106
|
if (memo) recordSignatures(symbolType(memo), memoSignatures);
|
|
@@ -265,26 +271,64 @@ export function validateNativeSignalNames(program, file) {
|
|
|
265
271
|
`Native signal handles and functions exposing handles or live reads must end in $. Rename ${JSON.stringify(text)} to ${JSON.stringify(text + '$')}; sampled values keep ordinary names.`,
|
|
266
272
|
);
|
|
267
273
|
}
|
|
268
|
-
function
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
274
|
+
function unwrapExpression(node) {
|
|
275
|
+
while (
|
|
276
|
+
node &&
|
|
277
|
+
(ts.isParenthesizedExpression(node) ||
|
|
278
|
+
ts.isAsExpression(node) ||
|
|
279
|
+
ts.isTypeAssertionExpression(node) ||
|
|
280
|
+
ts.isNonNullExpression(node) ||
|
|
281
|
+
ts.isSatisfiesExpression(node))
|
|
282
|
+
) {
|
|
283
|
+
node = node.expression;
|
|
284
|
+
}
|
|
285
|
+
return node;
|
|
286
|
+
}
|
|
287
|
+
function createsCapability(node) {
|
|
288
|
+
node = unwrapExpression(node);
|
|
289
|
+
if (!node) return false;
|
|
290
|
+
if (ts.isCallExpression(node)) return exposesHandle(checker.getTypeAtLocation(node));
|
|
291
|
+
return (
|
|
292
|
+
(ts.isArrowFunction(node) || ts.isFunctionExpression(node)) &&
|
|
293
|
+
(exposesHandle(checker.getTypeAtLocation(node)) || exposesLiveRead(node))
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
function isDomStyleProperty(node) {
|
|
297
|
+
let object = node.parent;
|
|
298
|
+
if (!ts.isObjectLiteralExpression(object)) return false;
|
|
299
|
+
while (
|
|
300
|
+
object.parent &&
|
|
301
|
+
(ts.isParenthesizedExpression(object.parent) ||
|
|
302
|
+
ts.isAsExpression(object.parent) ||
|
|
303
|
+
ts.isSatisfiesExpression(object.parent) ||
|
|
304
|
+
ts.isNonNullExpression(object.parent) ||
|
|
305
|
+
(ts.isBinaryExpression(object.parent) &&
|
|
306
|
+
(object.parent.operatorToken.kind === ts.SyntaxKind.BarBarToken ||
|
|
307
|
+
object.parent.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken ||
|
|
308
|
+
(object.parent.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken &&
|
|
309
|
+
object.parent.right === object))) ||
|
|
310
|
+
(ts.isConditionalExpression(object.parent) &&
|
|
311
|
+
(object.parent.whenTrue === object || object.parent.whenFalse === object)))
|
|
312
|
+
)
|
|
313
|
+
object = object.parent;
|
|
314
|
+
const container = object.parent;
|
|
315
|
+
if (!container || !ts.isJsxExpression(container)) return false;
|
|
316
|
+
const attribute = container.parent;
|
|
317
|
+
if (!ts.isJsxAttribute(attribute) || attribute.name.getText(sourceFile) !== 'style')
|
|
318
|
+
return false;
|
|
319
|
+
const tag = attribute.parent.parent.tagName;
|
|
320
|
+
return ts.isIdentifier(tag) && /^[a-z]/.test(tag.text);
|
|
272
321
|
}
|
|
273
322
|
function visit(node) {
|
|
274
|
-
if (ts.isVariableDeclaration(node)
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (!node.isTypeOnly && !node.parent.parent.isTypeOnly) checkName(node.name);
|
|
278
|
-
} else if (ts.isExportSpecifier(node)) {
|
|
279
|
-
if (valueExport(node)) checkName(node.name, node.propertyName ?? node.name);
|
|
323
|
+
if (ts.isVariableDeclaration(node)) {
|
|
324
|
+
if (node.initializer && createsCapability(node.initializer))
|
|
325
|
+
checkName(node.name, node.initializer);
|
|
280
326
|
} else if (ts.isPropertyAssignment(node)) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
) {
|
|
287
|
-
checkName(node.name);
|
|
327
|
+
if (!isDomStyleProperty(node) && createsCapability(node.initializer))
|
|
328
|
+
checkName(node.name, node.initializer);
|
|
329
|
+
} else if (ts.isPropertyDeclaration(node)) {
|
|
330
|
+
if (node.initializer && createsCapability(node.initializer))
|
|
331
|
+
checkName(node.name, node.initializer);
|
|
288
332
|
} else if (
|
|
289
333
|
ts.isFunctionDeclaration(node) ||
|
|
290
334
|
ts.isMethodDeclaration(node) ||
|
|
@@ -295,9 +339,11 @@ export function validateNativeSignalNames(program, file) {
|
|
|
295
339
|
ts.isBinaryExpression(node) &&
|
|
296
340
|
node.operatorToken.kind === ts.SyntaxKind.EqualsToken
|
|
297
341
|
) {
|
|
298
|
-
if (
|
|
299
|
-
|
|
300
|
-
|
|
342
|
+
if (createsCapability(node.right)) {
|
|
343
|
+
if (ts.isPropertyAccessExpression(node.left)) checkName(node.left.name, node.right);
|
|
344
|
+
else if (ts.isElementAccessExpression(node.left))
|
|
345
|
+
checkName(node.left.argumentExpression, node.right);
|
|
346
|
+
}
|
|
301
347
|
} else if (ts.isCallExpression(node)) {
|
|
302
348
|
const declaration = checker.getResolvedSignature(node)?.declaration;
|
|
303
349
|
if (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** @import { ParseOptions } from '@tsrx/core/types' */
|
|
2
2
|
/** @import { Program } from 'estree' */
|
|
3
3
|
|
|
4
|
-
import { parseModule as parseNativeModule } from '
|
|
4
|
+
import { parseModule as parseNativeModule } from '@tsrx/oxc/tsrx-core-compat';
|
|
5
5
|
import { parseModule as parseJavaScriptModule } from './parser.browser.js';
|
|
6
6
|
|
|
7
7
|
import { isolateOutputOptions, publishOutput } from './parser-output.js';
|
|
@@ -8,6 +8,46 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { parseModule } from '@tsrx/core';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Share renderer ownership across diagnostics for one adopted parser tree.
|
|
13
|
+
* The lazy proof lives only for that analysis and never reparses editor input.
|
|
14
|
+
*/
|
|
15
|
+
export function createRendererRegionResolver(ast, source, filename, options = {}) {
|
|
16
|
+
const owner = options.renderer?.id ?? (options.dom === false ? null : 'dom');
|
|
17
|
+
let regions;
|
|
18
|
+
return function isDOM(node) {
|
|
19
|
+
let renderer = owner;
|
|
20
|
+
if (regions === undefined) {
|
|
21
|
+
regions =
|
|
22
|
+
options.rendererBoundaries && Object.keys(options.rendererBoundaries).length > 0
|
|
23
|
+
? analyzeRendererBoundaries(source, {
|
|
24
|
+
ast,
|
|
25
|
+
filename,
|
|
26
|
+
rendererBoundaries: options.rendererBoundaries,
|
|
27
|
+
}).boundaries
|
|
28
|
+
: [];
|
|
29
|
+
}
|
|
30
|
+
let narrowest = Infinity;
|
|
31
|
+
for (const boundary of regions) {
|
|
32
|
+
const range = boundary.region?.range ?? boundary.region?.valueRange;
|
|
33
|
+
if (
|
|
34
|
+
!range ||
|
|
35
|
+
node.start < range[0] ||
|
|
36
|
+
node.end > range[1] ||
|
|
37
|
+
range[1] - range[0] >= narrowest
|
|
38
|
+
)
|
|
39
|
+
continue;
|
|
40
|
+
renderer = boundary.childRenderer;
|
|
41
|
+
narrowest = range[1] - range[0];
|
|
42
|
+
}
|
|
43
|
+
return (
|
|
44
|
+
renderer === 'dom' ||
|
|
45
|
+
options.rendererRegistry?.[renderer]?.target === 'dom' ||
|
|
46
|
+
(renderer === options.renderer?.id && options.renderer?.target === 'dom')
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
11
51
|
const TRANSPARENT_TS_EXPRESSIONS = new Set([
|
|
12
52
|
'ParenthesizedExpression',
|
|
13
53
|
'TSAsExpression',
|