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,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 {
|