octane 0.2.12 → 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/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-tables.cjs +4 -94
- package/dist/cjs/error-codes.client.generated.cjs +33 -0
- package/dist/cjs/error-codes.server.generated.cjs +19 -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 +6 -0
- package/dist/cjs/runtime.cjs +2791 -215
- package/dist/cjs/runtime.server.cjs +716 -76
- package/dist/cjs/server/early-signals.cjs +45 -0
- package/dist/cjs/server/index.cjs +44 -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 +163 -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 +32 -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/version.cjs +1 -1
- package/dist/class-names.d.ts +5 -0
- package/dist/class-names.js +25 -0
- package/dist/compiler/bundler.js +34 -6
- package/dist/compiler/compile.js +2234 -559
- 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 +26 -0
- package/dist/compiler/hydrate-boundaries.js +358 -15
- package/dist/compiler/index.d.ts +38 -1
- package/dist/compiler/index.js +1 -0
- package/dist/compiler/native-attribute-reads.js +127 -0
- package/dist/compiler/native-read-diagnostics.js +48 -9
- package/dist/compiler/native-read-types.js +55 -32
- 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.js +57 -10
- 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.d.ts +6 -1
- package/dist/compiler/volar.js +674 -143
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +10 -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-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 +5 -0
- package/dist/error-codes.server.generated.js +19 -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 +2 -0
- package/dist/index.js +4 -0
- package/dist/internal/signal-read.d.ts +1 -0
- package/dist/internal/signal-read.js +4 -0
- package/dist/jsx-runtime.d.ts +45 -6
- package/dist/public-types.d.ts +15 -1
- package/dist/runtime.d.ts +259 -21
- package/dist/runtime.js +2812 -220
- package/dist/runtime.server.d.ts +86 -6
- package/dist/runtime.server.js +712 -76
- package/dist/server/early-signals.d.ts +14 -0
- package/dist/server/early-signals.js +34 -0
- package/dist/server/index.d.ts +10 -2
- package/dist/server/index.js +48 -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 +50 -5
- package/dist/signals/graph.js +170 -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 +32 -3
- package/dist/signals/read-protocol.js +26 -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/version.js +1 -1
- package/package.json +37 -1
package/dist/compiler/compile.js
CHANGED
|
@@ -40,6 +40,12 @@ import {
|
|
|
40
40
|
withDeferredImports,
|
|
41
41
|
} from '@tsrx/core';
|
|
42
42
|
import { parseModule } from '#octane/compiler-parser';
|
|
43
|
+
import {
|
|
44
|
+
collectServerFunctionNodes,
|
|
45
|
+
serverContextTypeImports,
|
|
46
|
+
serverFunctionContextIndex,
|
|
47
|
+
unwrapServerFunctionInitializer,
|
|
48
|
+
} from './server-context.js';
|
|
43
49
|
import { createStyleScopePass } from './style-scopes.js';
|
|
44
50
|
import { decodeHTMLStrict } from 'entities';
|
|
45
51
|
import { print as esrapPrint } from 'esrap';
|
|
@@ -47,6 +53,7 @@ import esrapTsx from 'esrap/languages/tsx';
|
|
|
47
53
|
import { buildFatSegments } from './fat-segments.js';
|
|
48
54
|
import {
|
|
49
55
|
METHOD_DEP_IMPORT,
|
|
56
|
+
analyzeCallbackDependencies,
|
|
50
57
|
analyzeHookDependencies,
|
|
51
58
|
applyHookDependencies,
|
|
52
59
|
isInvariantLiteral,
|
|
@@ -93,7 +100,24 @@ import {
|
|
|
93
100
|
wrapNativeReadScope,
|
|
94
101
|
wrapNativeWarmScope,
|
|
95
102
|
} from './native-read-codegen.js';
|
|
96
|
-
import { createTextTypeFactsLookup } from './text-type-facts.js';
|
|
103
|
+
import { createTextTypeFactsLookup, normalizeTextTypeFilename } from './text-type-facts.js';
|
|
104
|
+
import { lowerSignalDeclarations } from './signal-declarations.js';
|
|
105
|
+
import { lowerSignalAttemptReads } from './signal-attempt-reads.js';
|
|
106
|
+
import { startIndependentSignalReads } from './signal-start-reads.js';
|
|
107
|
+
import {
|
|
108
|
+
isDirectSignalHandleExpression,
|
|
109
|
+
lowerNativeAttributeReads,
|
|
110
|
+
} from './native-attribute-reads.js';
|
|
111
|
+
import { prepareDomBindings } from './dom-bindings.js';
|
|
112
|
+
import { parseDomBindingRequest } from './dom-binding-request.js';
|
|
113
|
+
import {
|
|
114
|
+
createTemplateIr,
|
|
115
|
+
appendTemplatePart,
|
|
116
|
+
appendTemplateIr,
|
|
117
|
+
templatePart,
|
|
118
|
+
templateElement,
|
|
119
|
+
serializeTemplateIr,
|
|
120
|
+
} from './template-ir.js';
|
|
97
121
|
import { collectProvenContextBindings, isProvenContextUse } from './context-use.js';
|
|
98
122
|
import { applyCssModuleConstants } from './css-module-constants.js';
|
|
99
123
|
import { assertUniversalRuntimeTarget, normalizeUniversalRuntime } from './universal-runtime.js';
|
|
@@ -507,6 +531,135 @@ function attrBindingUpdateHelper(bind, inlineBindingGuards = false) {
|
|
|
507
531
|
}
|
|
508
532
|
}
|
|
509
533
|
|
|
534
|
+
function canCarryDirectSignalHandle(node) {
|
|
535
|
+
if (!node || node.metadata?.octane_string_child || node.metadata?.octane_primitive_text_child) {
|
|
536
|
+
return false;
|
|
537
|
+
}
|
|
538
|
+
if (
|
|
539
|
+
node.type === 'TSAsExpression' ||
|
|
540
|
+
node.type === 'TSTypeAssertion' ||
|
|
541
|
+
node.type === 'TSNonNullExpression' ||
|
|
542
|
+
node.type === 'ParenthesizedExpression' ||
|
|
543
|
+
node.type === 'ChainExpression'
|
|
544
|
+
) {
|
|
545
|
+
return canCarryDirectSignalHandle(node.expression);
|
|
546
|
+
}
|
|
547
|
+
if (node.type === 'ConditionalExpression') {
|
|
548
|
+
return (
|
|
549
|
+
canCarryDirectSignalHandle(node.consequent) || canCarryDirectSignalHandle(node.alternate)
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
if (node.type === 'LogicalExpression') {
|
|
553
|
+
return canCarryDirectSignalHandle(node.left) || canCarryDirectSignalHandle(node.right);
|
|
554
|
+
}
|
|
555
|
+
if (node.type === 'SequenceExpression') {
|
|
556
|
+
return canCarryDirectSignalHandle(node.expressions.at(-1));
|
|
557
|
+
}
|
|
558
|
+
if (
|
|
559
|
+
(node.type === 'CallExpression' || node.type === 'OptionalCallExpression') &&
|
|
560
|
+
(node.callee?.type === 'MemberExpression' ||
|
|
561
|
+
node.callee?.type === 'OptionalMemberExpression') &&
|
|
562
|
+
(node.callee.computed
|
|
563
|
+
? node.callee.property?.type === 'Literal' && node.callee.property.value === 'get'
|
|
564
|
+
: node.callee.property?.name === 'get')
|
|
565
|
+
) {
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
return ![
|
|
569
|
+
'Literal',
|
|
570
|
+
'TemplateLiteral',
|
|
571
|
+
'UnaryExpression',
|
|
572
|
+
'BinaryExpression',
|
|
573
|
+
'UpdateExpression',
|
|
574
|
+
'ObjectExpression',
|
|
575
|
+
'ArrayExpression',
|
|
576
|
+
'FunctionExpression',
|
|
577
|
+
'ArrowFunctionExpression',
|
|
578
|
+
'ClassExpression',
|
|
579
|
+
'JSXElement',
|
|
580
|
+
'JSXFragment',
|
|
581
|
+
].includes(node.type);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function spreadContainsDirectSignalHandle(node) {
|
|
585
|
+
if (isDirectSignalHandleExpression(node)) return true;
|
|
586
|
+
while (
|
|
587
|
+
node &&
|
|
588
|
+
(node.type === 'TSAsExpression' ||
|
|
589
|
+
node.type === 'TSTypeAssertion' ||
|
|
590
|
+
node.type === 'TSNonNullExpression')
|
|
591
|
+
) {
|
|
592
|
+
node = node.expression;
|
|
593
|
+
}
|
|
594
|
+
if (node?.type === 'ObjectExpression') {
|
|
595
|
+
return (node.properties ?? []).some((property) => {
|
|
596
|
+
if (property.type === 'SpreadElement' || property.type === 'SpreadProperty') {
|
|
597
|
+
return spreadContainsDirectSignalHandle(property.argument);
|
|
598
|
+
}
|
|
599
|
+
return spreadContainsDirectSignalHandle(property.value);
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
if (node?.type === 'ArrayExpression') {
|
|
603
|
+
return (node.elements ?? []).some(spreadContainsDirectSignalHandle);
|
|
604
|
+
}
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function directSignalSite(ctx, node, kind = 'binding') {
|
|
609
|
+
const position = node?.start ?? `${node?.loc?.start?.line ?? 0}:${node?.loc?.start?.column ?? 0}`;
|
|
610
|
+
// Branch/loop helper names differ between the client and server compilers.
|
|
611
|
+
// Identity belongs to the authored site, not its generated function owner.
|
|
612
|
+
return `${kind === 'input' ? 'i' : 'b'}:${strongHash(
|
|
613
|
+
`octane:signal-${kind}-site:2\0${normalizeTextTypeFilename(ctx.filename) ?? ctx.filename}\0${position}`,
|
|
614
|
+
)}`;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function componentInvocationSite(ctx, node) {
|
|
618
|
+
const position = node?.start ?? `${node?.loc?.start?.line ?? 0}:${node?.loc?.start?.column ?? 0}`;
|
|
619
|
+
// Authored positions identify call sites independently of the server/client
|
|
620
|
+
// helper names introduced for loops, branches, and value-position fragments.
|
|
621
|
+
return `c:${strongHash(
|
|
622
|
+
`octane:component-invocation-site:1\0${normalizeTextTypeFilename(ctx.filename) ?? ctx.filename}\0${position}`,
|
|
623
|
+
)}`;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function markDirectSignalBinding(binding, ctx, origin, kind) {
|
|
627
|
+
if (!canCarryDirectSignalHandle(binding.expr)) return binding;
|
|
628
|
+
ctx.signalBindingsUsed = true;
|
|
629
|
+
if (binding.kind !== 'text' && binding.kind !== 'textOnlyChild') ctx.signalBindingsEager = true;
|
|
630
|
+
return {
|
|
631
|
+
...binding,
|
|
632
|
+
signalDirect: true,
|
|
633
|
+
signalSite: directSignalSite(ctx, origin, kind),
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function ssrSignalValue(node, ctx, origin, capability = false) {
|
|
638
|
+
if (!(capability ? canCarryDirectSignalHandle(node) : isDirectSignalHandleExpression(node))) {
|
|
639
|
+
return node;
|
|
640
|
+
}
|
|
641
|
+
ctx.signalBindingsUsed = true;
|
|
642
|
+
ctx.runtimeNeeded.add('ssrSignalValue');
|
|
643
|
+
return ssrCall('ssrSignalValue', [node], origin ?? node);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
function ssrSignalStyleObject(node, ctx, origin) {
|
|
647
|
+
// Non-native compilation only: preserve targeted binding reads without
|
|
648
|
+
// opting an otherwise ordinary module into a native renderer read frame.
|
|
649
|
+
if (node?.type !== 'ObjectExpression') return node;
|
|
650
|
+
let changed = false;
|
|
651
|
+
const properties = (node.properties ?? []).map((property) => {
|
|
652
|
+
if (property.type === 'SpreadElement' || property.type === 'SpreadProperty') {
|
|
653
|
+
return property;
|
|
654
|
+
}
|
|
655
|
+
const value = ssrSignalValue(property.value, ctx, origin ?? property, true);
|
|
656
|
+
if (value === property.value) return property;
|
|
657
|
+
changed = true;
|
|
658
|
+
return { ...property, value };
|
|
659
|
+
});
|
|
660
|
+
return changed ? { ...node, properties } : node;
|
|
661
|
+
}
|
|
662
|
+
|
|
510
663
|
/**
|
|
511
664
|
* An ALIAS: an authored range that emits nothing of its own but means the same
|
|
512
665
|
* thing as one that does. A component's closing tag is the case — `</Card>` is
|
|
@@ -878,8 +1031,8 @@ function hasPotentialDangerouslySetInnerHTML(node) {
|
|
|
878
1031
|
const attrs = node.attributes || node.openingElement?.attributes || [];
|
|
879
1032
|
return attrs.some(
|
|
880
1033
|
(attr) =>
|
|
881
|
-
attr.type === 'SpreadAttribute' ||
|
|
882
|
-
|
|
1034
|
+
((attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') &&
|
|
1035
|
+
!attr._octaneKnownAttributeSpread) ||
|
|
883
1036
|
((attr.type === 'Attribute' || attr.type === 'JSXAttribute') &&
|
|
884
1037
|
jsxAttrRawName(attr) === 'dangerouslySetInnerHTML'),
|
|
885
1038
|
);
|
|
@@ -889,6 +1042,7 @@ function hasDefinitelyPresentDirectDangerouslySetInnerHTML(node) {
|
|
|
889
1042
|
const attrs = node.attributes || node.openingElement?.attributes || [];
|
|
890
1043
|
let state = false;
|
|
891
1044
|
for (const attr of attrs) {
|
|
1045
|
+
if (attr._octaneKnownAttributeSpread) continue;
|
|
892
1046
|
if (attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') {
|
|
893
1047
|
// A later spread may overwrite or introduce the prop, so the effective
|
|
894
1048
|
// writer is no longer statically known. A following direct writer can
|
|
@@ -915,6 +1069,7 @@ function hasDefinitelyPresentDirectChildrenProp(node) {
|
|
|
915
1069
|
const attrs = node.attributes || node.openingElement?.attributes || [];
|
|
916
1070
|
let state = false;
|
|
917
1071
|
for (const attr of attrs) {
|
|
1072
|
+
if (attr._octaneKnownAttributeSpread) continue;
|
|
918
1073
|
if (attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') {
|
|
919
1074
|
state = null;
|
|
920
1075
|
continue;
|
|
@@ -1192,6 +1347,7 @@ const HOOK_MEMO_RUNTIME_HELPERS = new Set([
|
|
|
1192
1347
|
]);
|
|
1193
1348
|
const NATIVE_READ_RUNTIME_HELPERS = new Set([
|
|
1194
1349
|
'nativeStyleBinding',
|
|
1350
|
+
'nativeProjectionBinding',
|
|
1195
1351
|
'readNativeDomStyle',
|
|
1196
1352
|
'readNativeDomProps',
|
|
1197
1353
|
'enableNativeReadCollection',
|
|
@@ -1213,6 +1369,27 @@ const NATIVE_READ_RUNTIME_HELPERS = new Set([
|
|
|
1213
1369
|
'nativeCreateScopedElement',
|
|
1214
1370
|
]);
|
|
1215
1371
|
const INTERNAL_CLIENT_RUNTIME_HELPERS = new Set([
|
|
1372
|
+
'bindPresentationView',
|
|
1373
|
+
'beginPresentationHydration',
|
|
1374
|
+
'endPresentationHydration',
|
|
1375
|
+
'presentationWrite',
|
|
1376
|
+
'presentationHostWrite',
|
|
1377
|
+
'presentationStructure',
|
|
1378
|
+
'presentationFailure',
|
|
1379
|
+
'markBindingChildren',
|
|
1380
|
+
'bindingText',
|
|
1381
|
+
'bindingChildSlot',
|
|
1382
|
+
'setBindingClass',
|
|
1383
|
+
'setBindingClassIfChanged',
|
|
1384
|
+
'enableSignalBindings',
|
|
1385
|
+
'createElementAt',
|
|
1386
|
+
'createElementFromConfig',
|
|
1387
|
+
'bindSignalText',
|
|
1388
|
+
'bindSignalChild',
|
|
1389
|
+
'bindSignalAttribute',
|
|
1390
|
+
'bindSignalValue',
|
|
1391
|
+
'bindSignalChecked',
|
|
1392
|
+
'bindSignalHostPropSources',
|
|
1216
1393
|
'canSplitStyleProperties',
|
|
1217
1394
|
'styleObjectPrototype',
|
|
1218
1395
|
'replaceRef',
|
|
@@ -1235,6 +1412,18 @@ const INTERNAL_CLIENT_RUNTIME_HELPERS = new Set([
|
|
|
1235
1412
|
...NATIVE_READ_RUNTIME_HELPERS,
|
|
1236
1413
|
]);
|
|
1237
1414
|
const INTERNAL_SERVER_RUNTIME_HELPERS = new Set([
|
|
1415
|
+
'bindPresentationView',
|
|
1416
|
+
'ssrBindingHtml',
|
|
1417
|
+
'ssrBindingBlock',
|
|
1418
|
+
'ssrBindingChild',
|
|
1419
|
+
'ssrBindingKey',
|
|
1420
|
+
'ssrBindingClass',
|
|
1421
|
+
'enableServerSignalBindings',
|
|
1422
|
+
'createElementAt',
|
|
1423
|
+
'createElementFromConfig',
|
|
1424
|
+
'ssrSignalValue',
|
|
1425
|
+
'ssrSignalControlValue',
|
|
1426
|
+
'ssrSignalControlAttrs',
|
|
1238
1427
|
'ssrSpreadContent',
|
|
1239
1428
|
...NATIVE_READ_RUNTIME_HELPERS,
|
|
1240
1429
|
]);
|
|
@@ -1617,6 +1806,7 @@ function lowerImportedErrorBoundaries(ast) {
|
|
|
1617
1806
|
function inheritSoleCompRoot(bodyNodes, ctx) {
|
|
1618
1807
|
if (bodyNodes.length !== 1) return false;
|
|
1619
1808
|
const n = bodyNodes[0];
|
|
1809
|
+
if (n._octaneBindingSite !== undefined) return false;
|
|
1620
1810
|
if (n.type !== 'Element' || !isComponentTag(n)) return false;
|
|
1621
1811
|
// Returned-JSX extraction can merge an authored key/spread config into one
|
|
1622
1812
|
// spread hole. Its descriptor still needs its own keyed reconciliation range.
|
|
@@ -6835,6 +7025,19 @@ function wrapAsBlockStmt(node) {
|
|
|
6835
7025
|
return b.block([node]);
|
|
6836
7026
|
}
|
|
6837
7027
|
|
|
7028
|
+
// Native host children already lower a JSX ternary through the same @if ABI.
|
|
7029
|
+
// Keep that positional claim at the callers; root/fragment value holes remain
|
|
7030
|
+
// renderable values. Binding planning shares this exact arm conversion and only
|
|
7031
|
+
// adds the compiler-issued site receipt to the otherwise unchanged statement.
|
|
7032
|
+
function conditionalJsxStatement(expr) {
|
|
7033
|
+
if (!isConditionalJsx(expr)) return null;
|
|
7034
|
+
return {
|
|
7035
|
+
...b.if(expr.test, wrapAsBlockStmt(expr.consequent), wrapAsBlockStmt(expr.alternate)),
|
|
7036
|
+
loc: expr.loc,
|
|
7037
|
+
...(expr._octaneBindingSite ? { _octaneBindingSite: expr._octaneBindingSite } : {}),
|
|
7038
|
+
};
|
|
7039
|
+
}
|
|
7040
|
+
|
|
6838
7041
|
/** `xs.map(x => <li/>)` — detect so we can throw a useful "use for-of" error. */
|
|
6839
7042
|
function isJsxReturningMapCall(node) {
|
|
6840
7043
|
if (!node || node.type !== 'CallExpression') return false;
|
|
@@ -7338,6 +7541,7 @@ function anchorlessRootShape(node) {
|
|
|
7338
7541
|
* the hydrator can use it as the keyed item boundary without an item pair.
|
|
7339
7542
|
*/
|
|
7340
7543
|
function isSsrMarkerlessForItem(node) {
|
|
7544
|
+
if (node?._octaneBindingSite !== undefined) return false;
|
|
7341
7545
|
const body = node?.body?.body || [];
|
|
7342
7546
|
const jsxChildren = body.filter((s) => isJsxNode(s));
|
|
7343
7547
|
return jsxChildren.length === 1 && isPlainHostRoot(jsxChildren[0]);
|
|
@@ -7918,21 +8122,6 @@ function identifierName(node) {
|
|
|
7918
8122
|
return null;
|
|
7919
8123
|
}
|
|
7920
8124
|
|
|
7921
|
-
function unwrapServerFunctionInitializer(node) {
|
|
7922
|
-
let current = node;
|
|
7923
|
-
while (
|
|
7924
|
-
current &&
|
|
7925
|
-
(current.type === 'TSAsExpression' ||
|
|
7926
|
-
current.type === 'TSSatisfiesExpression' ||
|
|
7927
|
-
current.type === 'TSNonNullExpression' ||
|
|
7928
|
-
current.type === 'TypeCastExpression' ||
|
|
7929
|
-
current.type === 'ParenthesizedExpression')
|
|
7930
|
-
) {
|
|
7931
|
-
current = current.expression;
|
|
7932
|
-
}
|
|
7933
|
-
return current;
|
|
7934
|
-
}
|
|
7935
|
-
|
|
7936
8125
|
function collectServerFunctionBindings(statements) {
|
|
7937
8126
|
const functions = new Set();
|
|
7938
8127
|
const aliases = new Map();
|
|
@@ -8095,6 +8284,8 @@ function analyzeServerModule(ast, filename) {
|
|
|
8095
8284
|
const statements = declaration.body?.body || [];
|
|
8096
8285
|
validateServerModuleIsolation(ast, declaration, filename);
|
|
8097
8286
|
const functionBindings = collectServerFunctionBindings(statements);
|
|
8287
|
+
const functionNodes = collectServerFunctionNodes(statements);
|
|
8288
|
+
const contextImports = serverContextTypeImports(statements);
|
|
8098
8289
|
for (const statement of statements) {
|
|
8099
8290
|
if (statement.type === 'ExportDefaultDeclaration') {
|
|
8100
8291
|
throw new Error('`module server` does not support default exports; use named functions.');
|
|
@@ -8151,7 +8342,15 @@ function analyzeServerModule(ast, filename) {
|
|
|
8151
8342
|
}
|
|
8152
8343
|
}
|
|
8153
8344
|
|
|
8154
|
-
|
|
8345
|
+
const contexts = new Map();
|
|
8346
|
+
for (const [exported, local] of exports) {
|
|
8347
|
+
const fn = functionNodes.get(local);
|
|
8348
|
+
if (fn === undefined) continue;
|
|
8349
|
+
const contextIndex = serverFunctionContextIndex(fn, contextImports, filename);
|
|
8350
|
+
if (contextIndex !== -1) contexts.set(exported, contextIndex);
|
|
8351
|
+
}
|
|
8352
|
+
|
|
8353
|
+
return { declaration, imports, exports, contexts, filename };
|
|
8155
8354
|
}
|
|
8156
8355
|
|
|
8157
8356
|
/**
|
|
@@ -8169,15 +8368,19 @@ function emitServerModuleClientStubs(info, ctx) {
|
|
|
8169
8368
|
if (!imported || !local) continue;
|
|
8170
8369
|
ctx.runtimeNeeded.add('__serverRpc');
|
|
8171
8370
|
const hash = strongHash(info.filename + '#' + imported);
|
|
8371
|
+
const contextIndex = info.contexts.get(imported);
|
|
8372
|
+
const rpcArgs =
|
|
8373
|
+
contextIndex === undefined
|
|
8374
|
+
? [b.literal(hash, JSON.stringify(hash)), b.id('args')]
|
|
8375
|
+
: [
|
|
8376
|
+
b.literal(hash, JSON.stringify(hash)),
|
|
8377
|
+
b.call(b.member(b.id('args'), 'slice'), b.literal(0), b.literal(contextIndex)),
|
|
8378
|
+
b.member(b.id('args'), b.literal(contextIndex), true),
|
|
8379
|
+
b.literal(true, 'true'),
|
|
8380
|
+
];
|
|
8172
8381
|
nodes.push(
|
|
8173
8382
|
inheritOriginLoc(
|
|
8174
|
-
b.const(
|
|
8175
|
-
local,
|
|
8176
|
-
b.arrow(
|
|
8177
|
-
[b.rest(b.id('args'))],
|
|
8178
|
-
b.call('_$__serverRpc', b.literal(hash, JSON.stringify(hash)), b.id('args')),
|
|
8179
|
-
),
|
|
8180
|
-
),
|
|
8383
|
+
b.const(local, b.arrow([b.rest(b.id('args'))], b.call('_$__serverRpc', ...rpcArgs))),
|
|
8181
8384
|
specifier,
|
|
8182
8385
|
),
|
|
8183
8386
|
);
|
|
@@ -8244,13 +8447,29 @@ function emitServerModuleServerNodes(info, ctx) {
|
|
|
8244
8447
|
...namespaceBody,
|
|
8245
8448
|
];
|
|
8246
8449
|
for (const [exported, local] of info.exports) {
|
|
8450
|
+
const contextIndex = info.contexts.get(exported);
|
|
8451
|
+
let value = b.id(local);
|
|
8452
|
+
if (contextIndex !== undefined) {
|
|
8453
|
+
ctx.runtimeNeeded.add('__registerServerFunction');
|
|
8454
|
+
const hash = strongHash(info.filename + '#' + exported);
|
|
8455
|
+
value = b.call(
|
|
8456
|
+
'_$__registerServerFunction',
|
|
8457
|
+
value,
|
|
8458
|
+
b.literal(contextIndex),
|
|
8459
|
+
b.object([
|
|
8460
|
+
b.prop('init', b.id('id'), b.literal(hash, JSON.stringify(hash))),
|
|
8461
|
+
b.prop('init', b.id('module'), b.literal(info.filename, JSON.stringify(info.filename))),
|
|
8462
|
+
b.prop('init', b.id('export'), b.literal(exported, JSON.stringify(exported))),
|
|
8463
|
+
]),
|
|
8464
|
+
);
|
|
8465
|
+
}
|
|
8247
8466
|
serverStatements.push(
|
|
8248
8467
|
inheritOriginLoc(
|
|
8249
8468
|
b.stmt(
|
|
8250
8469
|
b.assignment(
|
|
8251
8470
|
'=',
|
|
8252
8471
|
b.member(b.id('server'), b.literal(exported, JSON.stringify(exported)), true),
|
|
8253
|
-
|
|
8472
|
+
value,
|
|
8254
8473
|
),
|
|
8255
8474
|
),
|
|
8256
8475
|
origin,
|
|
@@ -8889,11 +9108,326 @@ export function compile(source, filename, options) {
|
|
|
8889
9108
|
return compileAuthored(source, filename, options, null);
|
|
8890
9109
|
}
|
|
8891
9110
|
|
|
9111
|
+
// A provider may describe an imported pure attribute factory's exact stable own
|
|
9112
|
+
// data fields. The import binding, not a spelling such as `stylex.attrs`, is authority.
|
|
9113
|
+
function markKnownAttributeSpreads(ast, contracts) {
|
|
9114
|
+
if (contracts === undefined) return ast;
|
|
9115
|
+
if (!Array.isArray(contracts)) throw new TypeError('knownAttributeSpreads must be an array.');
|
|
9116
|
+
if (contracts.length === 0) return ast;
|
|
9117
|
+
const paths = new Set();
|
|
9118
|
+
const shorthandProviders = new Map();
|
|
9119
|
+
for (const contract of contracts) {
|
|
9120
|
+
if (
|
|
9121
|
+
!contract ||
|
|
9122
|
+
typeof contract.source !== 'string' ||
|
|
9123
|
+
!contract.source ||
|
|
9124
|
+
typeof contract.imported !== 'string' ||
|
|
9125
|
+
!contract.imported ||
|
|
9126
|
+
!Array.isArray(contract.fields) ||
|
|
9127
|
+
contract.fields.length === 0 ||
|
|
9128
|
+
new Set(contract.fields).size !== contract.fields.length ||
|
|
9129
|
+
contract.fields.some(
|
|
9130
|
+
(field) =>
|
|
9131
|
+
typeof field !== 'string' ||
|
|
9132
|
+
!/^(?:class|className|style|title|role|aria-[a-z][a-z0-9_-]*|data-[a-z][a-z0-9_-]*)$/.test(
|
|
9133
|
+
field,
|
|
9134
|
+
) ||
|
|
9135
|
+
field.startsWith('data-octane-'),
|
|
9136
|
+
) ||
|
|
9137
|
+
(contract.members !== undefined &&
|
|
9138
|
+
(!Array.isArray(contract.members) ||
|
|
9139
|
+
contract.members.some(
|
|
9140
|
+
(member) => typeof member !== 'string' || !/^[A-Za-z_$][\w$]*$/.test(member),
|
|
9141
|
+
))) ||
|
|
9142
|
+
(contract.imported === '*' && !contract.members?.length) ||
|
|
9143
|
+
(contract.style !== undefined &&
|
|
9144
|
+
(contract.style !== 'object' || !contract.fields.includes('style')))
|
|
9145
|
+
)
|
|
9146
|
+
throw new TypeError(
|
|
9147
|
+
'Invalid knownAttributeSpreads import or fixed native attribute contract.',
|
|
9148
|
+
);
|
|
9149
|
+
const path = JSON.stringify([contract.source, contract.imported, contract.members ?? []]);
|
|
9150
|
+
if (paths.has(path))
|
|
9151
|
+
throw new TypeError(
|
|
9152
|
+
'Duplicate knownAttributeSpreads contract for the same imported factory.',
|
|
9153
|
+
);
|
|
9154
|
+
paths.add(path);
|
|
9155
|
+
if (contract.jsxAttribute !== undefined) {
|
|
9156
|
+
if (
|
|
9157
|
+
typeof contract.jsxAttribute !== 'string' ||
|
|
9158
|
+
!/^[A-Za-z_$][\w$-]*$/.test(contract.jsxAttribute) ||
|
|
9159
|
+
shorthandProviders.has(contract.jsxAttribute)
|
|
9160
|
+
)
|
|
9161
|
+
throw new TypeError('Invalid or ambiguous knownAttributeSpreads jsxAttribute.');
|
|
9162
|
+
shorthandProviders.set(contract.jsxAttribute, contract);
|
|
9163
|
+
}
|
|
9164
|
+
}
|
|
9165
|
+
const imports = new Map();
|
|
9166
|
+
for (const declaration of ast.body) {
|
|
9167
|
+
if (declaration.type !== 'ImportDeclaration' || declaration.importKind === 'type') continue;
|
|
9168
|
+
for (const specifier of declaration.specifiers) {
|
|
9169
|
+
if (specifier.importKind === 'type') continue;
|
|
9170
|
+
const imported =
|
|
9171
|
+
specifier.type === 'ImportNamespaceSpecifier'
|
|
9172
|
+
? '*'
|
|
9173
|
+
: specifier.type === 'ImportDefaultSpecifier'
|
|
9174
|
+
? 'default'
|
|
9175
|
+
: (specifier.imported.name ?? specifier.imported.value);
|
|
9176
|
+
const matches = contracts.filter(
|
|
9177
|
+
(contract) =>
|
|
9178
|
+
contract.source === declaration.source.value && contract.imported === imported,
|
|
9179
|
+
);
|
|
9180
|
+
if (matches.length) imports.set(specifier.local.name, matches);
|
|
9181
|
+
}
|
|
9182
|
+
}
|
|
9183
|
+
const shorthandAttributes = new Map();
|
|
9184
|
+
const usedNames = new Set();
|
|
9185
|
+
if (shorthandProviders.size > 0) {
|
|
9186
|
+
mapAst(ast, (node) => {
|
|
9187
|
+
if (node.type === 'Identifier' || node.type === 'JSXIdentifier') usedNames.add(node.name);
|
|
9188
|
+
if (node.type !== 'JSXOpeningElement' && node.type !== 'Element') return null;
|
|
9189
|
+
const name = node.type === 'Element' ? node.id : node.name;
|
|
9190
|
+
if (
|
|
9191
|
+
(name?.type !== 'JSXIdentifier' && name?.type !== 'Identifier') ||
|
|
9192
|
+
typeof name.name !== 'string' ||
|
|
9193
|
+
name.name[0] !== name.name[0].toLowerCase()
|
|
9194
|
+
)
|
|
9195
|
+
return null;
|
|
9196
|
+
const seen = new Set();
|
|
9197
|
+
for (const attr of node.attributes ?? []) {
|
|
9198
|
+
if (attr.type !== 'JSXAttribute' && attr.type !== 'Attribute') continue;
|
|
9199
|
+
const raw = attr.name?.name ?? attr.name;
|
|
9200
|
+
const contract = shorthandProviders.get(raw);
|
|
9201
|
+
if (
|
|
9202
|
+
!contract ||
|
|
9203
|
+
seen.has(raw) ||
|
|
9204
|
+
attr.value?.type !== 'JSXExpressionContainer' ||
|
|
9205
|
+
attr.value.expression?.type === 'JSXEmptyExpression'
|
|
9206
|
+
)
|
|
9207
|
+
continue;
|
|
9208
|
+
seen.add(raw);
|
|
9209
|
+
shorthandAttributes.set(attr, contract);
|
|
9210
|
+
}
|
|
9211
|
+
return null;
|
|
9212
|
+
});
|
|
9213
|
+
}
|
|
9214
|
+
if (imports.size === 0 && shorthandAttributes.size === 0) return ast;
|
|
9215
|
+
const lexical = createLexicalAnalysis(ast);
|
|
9216
|
+
const addedImports = [];
|
|
9217
|
+
const addedBindings = new Map();
|
|
9218
|
+
let nativeReadName;
|
|
9219
|
+
let hasProjection = false;
|
|
9220
|
+
const read = (node) => {
|
|
9221
|
+
if (nativeReadName === undefined) {
|
|
9222
|
+
nativeReadName = '_nativeAttributeValue';
|
|
9223
|
+
for (let suffix = 1; usedNames.has(nativeReadName); suffix++)
|
|
9224
|
+
nativeReadName = `_nativeAttributeValue${suffix}`;
|
|
9225
|
+
usedNames.add(nativeReadName);
|
|
9226
|
+
addedImports.push(
|
|
9227
|
+
inheritOriginLoc(
|
|
9228
|
+
b.imports([['readNativeDomValue', nativeReadName]], 'octane/internal/signal-read'),
|
|
9229
|
+
node,
|
|
9230
|
+
),
|
|
9231
|
+
);
|
|
9232
|
+
}
|
|
9233
|
+
const call = inheritOriginLoc(b.call(b.id(nativeReadName), node), node);
|
|
9234
|
+
call.metadata = { octaneNativeSignalRead: true };
|
|
9235
|
+
return call;
|
|
9236
|
+
};
|
|
9237
|
+
const result = mapAst(ast, (node) => {
|
|
9238
|
+
const shorthand = shorthandAttributes.get(node);
|
|
9239
|
+
if (shorthand) {
|
|
9240
|
+
let local;
|
|
9241
|
+
for (const [name, matches] of imports) {
|
|
9242
|
+
if (
|
|
9243
|
+
matches.includes(shorthand) &&
|
|
9244
|
+
lexical.resolveBinding(lexical.nodeScopes.get(node), name)?.scope === lexical.rootScope
|
|
9245
|
+
) {
|
|
9246
|
+
local = name;
|
|
9247
|
+
break;
|
|
9248
|
+
}
|
|
9249
|
+
}
|
|
9250
|
+
if (local === undefined) {
|
|
9251
|
+
local = addedBindings.get(shorthand);
|
|
9252
|
+
if (local === undefined) {
|
|
9253
|
+
local = '_jsxAttribute';
|
|
9254
|
+
for (let suffix = 1; usedNames.has(local); suffix++) local = `_jsxAttribute${suffix}`;
|
|
9255
|
+
usedNames.add(local);
|
|
9256
|
+
addedBindings.set(shorthand, local);
|
|
9257
|
+
addedImports.push(
|
|
9258
|
+
inheritOriginLoc(
|
|
9259
|
+
shorthand.imported === '*'
|
|
9260
|
+
? b.import_all(local, shorthand.source)
|
|
9261
|
+
: shorthand.imported === 'default'
|
|
9262
|
+
? b.import_declaration(
|
|
9263
|
+
[
|
|
9264
|
+
{
|
|
9265
|
+
type: 'ImportDefaultSpecifier',
|
|
9266
|
+
local: b.id(local),
|
|
9267
|
+
metadata: { path: [] },
|
|
9268
|
+
},
|
|
9269
|
+
],
|
|
9270
|
+
shorthand.source,
|
|
9271
|
+
)
|
|
9272
|
+
: b.imports([[shorthand.imported, local]], shorthand.source),
|
|
9273
|
+
node,
|
|
9274
|
+
),
|
|
9275
|
+
);
|
|
9276
|
+
}
|
|
9277
|
+
}
|
|
9278
|
+
let callee = b.id(local);
|
|
9279
|
+
for (const member of shorthand.members ?? []) callee = b.member(callee, b.id(member));
|
|
9280
|
+
const lifted = lowerNativeAttributeReads(node.value.expression, read);
|
|
9281
|
+
hasProjection ||= lifted.reactive;
|
|
9282
|
+
return {
|
|
9283
|
+
...node,
|
|
9284
|
+
value: {
|
|
9285
|
+
...node.value,
|
|
9286
|
+
expression: inheritOriginLoc(b.call(callee, lifted.expression), node),
|
|
9287
|
+
},
|
|
9288
|
+
_octaneKnownAttributeSpread: {
|
|
9289
|
+
fields: [...shorthand.fields],
|
|
9290
|
+
style: shorthand.style,
|
|
9291
|
+
...(lifted.reactive ? { projection: true } : {}),
|
|
9292
|
+
},
|
|
9293
|
+
};
|
|
9294
|
+
}
|
|
9295
|
+
if (node.type !== 'JSXSpreadAttribute' && node.type !== 'SpreadAttribute') return null;
|
|
9296
|
+
const call = node.argument;
|
|
9297
|
+
if (call?.type !== 'CallExpression' || call.optional) return null;
|
|
9298
|
+
let callee = call.callee;
|
|
9299
|
+
const members = [];
|
|
9300
|
+
while (callee?.type === 'MemberExpression' && !callee.computed && !callee.optional) {
|
|
9301
|
+
members.unshift(callee.property.name);
|
|
9302
|
+
callee = callee.object;
|
|
9303
|
+
}
|
|
9304
|
+
if (callee?.type !== 'Identifier') return null;
|
|
9305
|
+
const matches = imports.get(callee.name);
|
|
9306
|
+
if (
|
|
9307
|
+
!matches ||
|
|
9308
|
+
lexical.resolveBinding(lexical.nodeScopes.get(callee), callee.name)?.scope !==
|
|
9309
|
+
lexical.rootScope
|
|
9310
|
+
)
|
|
9311
|
+
return null;
|
|
9312
|
+
const contract = matches.find((candidate) => {
|
|
9313
|
+
const expected = candidate.members ?? [];
|
|
9314
|
+
return (
|
|
9315
|
+
expected.length === members.length &&
|
|
9316
|
+
expected.every((member, index) => member === members[index])
|
|
9317
|
+
);
|
|
9318
|
+
});
|
|
9319
|
+
return contract
|
|
9320
|
+
? {
|
|
9321
|
+
...node,
|
|
9322
|
+
_octaneKnownAttributeSpread: { fields: [...contract.fields], style: contract.style },
|
|
9323
|
+
}
|
|
9324
|
+
: null;
|
|
9325
|
+
});
|
|
9326
|
+
if (hasProjection)
|
|
9327
|
+
mapAst(result, (node) => {
|
|
9328
|
+
if (node.type !== 'JSXOpeningElement' && node.type !== 'Element') return null;
|
|
9329
|
+
const attributes = node.attributes ?? [];
|
|
9330
|
+
const identity = (name) =>
|
|
9331
|
+
(name === 'className' ? 'class' : (ATTRIBUTE_ALIASES.get(name) ?? name)).toLowerCase();
|
|
9332
|
+
for (const attr of attributes) {
|
|
9333
|
+
if (!attr._octaneKnownAttributeSpread?.projection) continue;
|
|
9334
|
+
const fields = new Set(attr._octaneKnownAttributeSpread.fields.map(identity));
|
|
9335
|
+
for (const other of attributes) {
|
|
9336
|
+
if (other === attr) continue;
|
|
9337
|
+
const known = other._octaneKnownAttributeSpread;
|
|
9338
|
+
if (
|
|
9339
|
+
(known && known.fields.some((field) => fields.has(identity(field)))) ||
|
|
9340
|
+
(!known &&
|
|
9341
|
+
(other.type === 'JSXSpreadAttribute' ||
|
|
9342
|
+
other.type === 'SpreadAttribute' ||
|
|
9343
|
+
fields.has(identity(jsxAttrRawName(other)))))
|
|
9344
|
+
)
|
|
9345
|
+
throw new Error(
|
|
9346
|
+
'Native JSX signal projections cannot overlap other writers for their fields or an unknown spread.',
|
|
9347
|
+
);
|
|
9348
|
+
}
|
|
9349
|
+
}
|
|
9350
|
+
return null;
|
|
9351
|
+
});
|
|
9352
|
+
return addedImports.length > 0 || hasProjection
|
|
9353
|
+
? {
|
|
9354
|
+
...result,
|
|
9355
|
+
body: [...addedImports, ...result.body],
|
|
9356
|
+
...(hasProjection
|
|
9357
|
+
? { metadata: { ...result.metadata, octaneNativeAttributeProjection: true } }
|
|
9358
|
+
: {}),
|
|
9359
|
+
}
|
|
9360
|
+
: result;
|
|
9361
|
+
}
|
|
9362
|
+
|
|
9363
|
+
// Keep the factory evaluation at its authored native element/iteration. Each
|
|
9364
|
+
// declared field then uses the normal compiler writer; there is no dynamic key
|
|
9365
|
+
// dispatcher and no second factory call. These are compiler-owned IR cells.
|
|
9366
|
+
function expandKnownAttributeSpreads(attributes) {
|
|
9367
|
+
if (!attributes.some((attr) => attr._octaneKnownAttributeSpread)) return attributes;
|
|
9368
|
+
return attributes.flatMap((attr) => {
|
|
9369
|
+
if (!attr._octaneKnownAttributeSpread) return [attr];
|
|
9370
|
+
if (
|
|
9371
|
+
attr._octaneKnownAttributeSpread.projection &&
|
|
9372
|
+
attr._octaneKnownAttributeSpread.fields.some(
|
|
9373
|
+
(field) => field === 'class' || field === 'className',
|
|
9374
|
+
) &&
|
|
9375
|
+
attributes.some(
|
|
9376
|
+
(other) => other !== attr && ['class', 'className'].includes(jsxAttrRawName(other)),
|
|
9377
|
+
)
|
|
9378
|
+
) {
|
|
9379
|
+
throw new Error(
|
|
9380
|
+
'A reactive native attribute projection cannot share its class with scoped or authored classes. Compose classes in the projection.',
|
|
9381
|
+
);
|
|
9382
|
+
}
|
|
9383
|
+
const source = {
|
|
9384
|
+
binding: null,
|
|
9385
|
+
expression: null,
|
|
9386
|
+
projection: attr._octaneKnownAttributeSpread.projection === true,
|
|
9387
|
+
fields: attr._octaneKnownAttributeSpread.fields,
|
|
9388
|
+
};
|
|
9389
|
+
const argument = attr.argument ?? attr.value.expression;
|
|
9390
|
+
return [
|
|
9391
|
+
{ knownSource: source, argument },
|
|
9392
|
+
...attr._octaneKnownAttributeSpread.fields.map((field) => {
|
|
9393
|
+
const object = inheritOriginLoc(b.id('_knownAttributeSpread'), argument);
|
|
9394
|
+
object.metadata = { octaneKnownAttributeSource: source };
|
|
9395
|
+
const expression = inheritOriginLoc(
|
|
9396
|
+
b.conditional(
|
|
9397
|
+
b.binary('==', object, b.literal(null)),
|
|
9398
|
+
undefinedNode(),
|
|
9399
|
+
b.member(object, b.literal(field), true),
|
|
9400
|
+
),
|
|
9401
|
+
argument,
|
|
9402
|
+
);
|
|
9403
|
+
expression.metadata = { octaneKnownAttributeRead: true };
|
|
9404
|
+
const attribute = inheritOriginLoc(
|
|
9405
|
+
b.jsx_attribute(
|
|
9406
|
+
inheritOriginLoc(b.jsx_id(field), attr),
|
|
9407
|
+
b.jsx_expression_container(expression, argument),
|
|
9408
|
+
),
|
|
9409
|
+
attr,
|
|
9410
|
+
);
|
|
9411
|
+
if (source.projection) attribute._octaneKnownProjectionSource = source;
|
|
9412
|
+
return attribute;
|
|
9413
|
+
}),
|
|
9414
|
+
];
|
|
9415
|
+
});
|
|
9416
|
+
}
|
|
9417
|
+
|
|
9418
|
+
function resolveKnownAttributeSource(expression, valueOf) {
|
|
9419
|
+
if (!expression?.metadata?.octaneKnownAttributeRead) return expression;
|
|
9420
|
+
return mapAst(expression, (node) => {
|
|
9421
|
+
const source = node.type === 'Identifier' && node.metadata?.octaneKnownAttributeSource;
|
|
9422
|
+
return source ? inheritOriginLoc(valueOf(source), node) : null;
|
|
9423
|
+
});
|
|
9424
|
+
}
|
|
9425
|
+
|
|
8892
9426
|
// Internal bundler entry point. Unlike the old __hydratePrepared handoff, this
|
|
8893
9427
|
// always validates authored source and only exposes the hydrate slice that the
|
|
8894
9428
|
// same compilation already prepared for production void-export classification.
|
|
8895
9429
|
export function compileForBundler(source, filename, options) {
|
|
8896
|
-
const metadata = { hydrateAst: null, cssModuleConstantImports: [] };
|
|
9430
|
+
const metadata = { hydrateAst: null, cssModuleConstantImports: [], independentWidgets: [] };
|
|
8897
9431
|
const result = compileAuthored(source, filename, options, metadata);
|
|
8898
9432
|
return { result, ...metadata };
|
|
8899
9433
|
}
|
|
@@ -8904,7 +9438,7 @@ function compileAuthored(source, filename, options, bundlerMetadata) {
|
|
|
8904
9438
|
throw new Error(`Unknown compile mode "${mode}" — expected 'client' or 'server'.`);
|
|
8905
9439
|
}
|
|
8906
9440
|
const cleanFilename = cleanCompileFilename(filename);
|
|
8907
|
-
|
|
9441
|
+
let analyzedAst = markParserSensitiveHosts(
|
|
8908
9442
|
normalizeAuthoredJsxLiterals(parseModule(source, cleanFilename)),
|
|
8909
9443
|
);
|
|
8910
9444
|
analyzeTsrx(analyzedAst, cleanFilename);
|
|
@@ -8913,20 +9447,89 @@ function compileAuthored(source, filename, options, bundlerMetadata) {
|
|
|
8913
9447
|
assertNativeReadDiagnostics(analyzedAst, source, cleanFilename, options);
|
|
8914
9448
|
const strongAnalysis = assertStrongMode(analyzedAst, source, cleanFilename, options);
|
|
8915
9449
|
const strongModeEnabled = strongAnalysis?.enabled === true;
|
|
8916
|
-
|
|
9450
|
+
analyzedAst = markKnownAttributeSpreads(analyzedAst, options?.knownAttributeSpreads);
|
|
9451
|
+
if (analyzedAst.metadata?.octaneNativeAttributeProjection) {
|
|
9452
|
+
options = { ...options, nativeReads: true };
|
|
9453
|
+
assertNativeReadOptions(options);
|
|
9454
|
+
}
|
|
9455
|
+
const bindingRequest = parseDomBindingRequest(filename);
|
|
9456
|
+
const bindingExport = bindingRequest?.exportName ?? null;
|
|
9457
|
+
if (
|
|
9458
|
+
bindingExport !== null &&
|
|
9459
|
+
(mode !== 'client' ||
|
|
9460
|
+
hydrateBoundaryPathFromId(filename) !== null ||
|
|
9461
|
+
(options?.renderer?.target && options.renderer.target !== 'dom'))
|
|
9462
|
+
) {
|
|
9463
|
+
throw new Error(
|
|
9464
|
+
'Octane DOM binding queries require the client DOM target without a Hydrate query.',
|
|
9465
|
+
);
|
|
9466
|
+
}
|
|
9467
|
+
const hasDomBindings =
|
|
9468
|
+
bindingExport !== null ||
|
|
9469
|
+
source.includes('use dom bindings') ||
|
|
9470
|
+
source.includes('adoptBindings') ||
|
|
9471
|
+
source.includes('mountBindings');
|
|
9472
|
+
let bindingConstants;
|
|
9473
|
+
// Binding plans classify authored child expressions before signal lowering or
|
|
9474
|
+
// JSX extraction. Use the same source-bound proof pass here, exactly once;
|
|
9475
|
+
// query modules no longer contain the original child ranges after planning.
|
|
9476
|
+
const bindingAst = hasDomBindings
|
|
9477
|
+
? prepareDomBindings(
|
|
9478
|
+
applyStringChildProofs(analyzedAst, source, cleanFilename, options?.textTypeFacts),
|
|
9479
|
+
source,
|
|
9480
|
+
cleanFilename,
|
|
9481
|
+
bindingExport,
|
|
9482
|
+
{
|
|
9483
|
+
normalizeChildren,
|
|
9484
|
+
conditionalJsxStatement,
|
|
9485
|
+
isText: isKnownTextChildExpression,
|
|
9486
|
+
canBorrowSsrHostBranchRange,
|
|
9487
|
+
analyzeCallbackDependencies,
|
|
9488
|
+
canCarryDirectSignalHandle,
|
|
9489
|
+
isDirectSignalHandleExpression,
|
|
9490
|
+
bakeStaticAttr,
|
|
9491
|
+
escapeHtml,
|
|
9492
|
+
mount: bindingRequest?.mount ?? false,
|
|
9493
|
+
props: bindingRequest?.props ?? null,
|
|
9494
|
+
...(mode === 'client' &&
|
|
9495
|
+
!options?.dev &&
|
|
9496
|
+
!options?.hmr &&
|
|
9497
|
+
(!options?.renderer?.target || options.renderer.target === 'dom')
|
|
9498
|
+
? {
|
|
9499
|
+
collectConstants(names) {
|
|
9500
|
+
if (names.length > 0)
|
|
9501
|
+
bindingConstants = {
|
|
9502
|
+
version: 1,
|
|
9503
|
+
source: strongHash(source),
|
|
9504
|
+
names,
|
|
9505
|
+
};
|
|
9506
|
+
},
|
|
9507
|
+
}
|
|
9508
|
+
: null),
|
|
9509
|
+
},
|
|
9510
|
+
)
|
|
9511
|
+
: analyzedAst;
|
|
9512
|
+
const attemptAst = lowerSignalAttemptReads(bindingAst);
|
|
9513
|
+
const signalAst = lowerSignalDeclarations(
|
|
9514
|
+
startIndependentSignalReads(
|
|
9515
|
+
attemptAst,
|
|
9516
|
+
options?.textTypeFacts || (options?.renderer?.target && options.renderer.target !== 'dom')
|
|
9517
|
+
? null
|
|
9518
|
+
: isKnownTextChildExpression,
|
|
9519
|
+
),
|
|
9520
|
+
cleanFilename,
|
|
9521
|
+
);
|
|
9522
|
+
if (bundlerMetadata !== null) bundlerMetadata.hydrateAst = signalAst;
|
|
8917
9523
|
const memoizedAst = strongModeEnabled
|
|
8918
|
-
? applyStrongAutomaticMemo(
|
|
9524
|
+
? applyStrongAutomaticMemo(signalAst, {
|
|
8919
9525
|
...options,
|
|
8920
9526
|
filename: cleanFilename,
|
|
8921
9527
|
strongHookAnalysis: strongAnalysis.strongHookAnalysis,
|
|
8922
9528
|
})
|
|
8923
|
-
:
|
|
8924
|
-
const textTypedAst =
|
|
8925
|
-
memoizedAst
|
|
8926
|
-
source,
|
|
8927
|
-
cleanFilename,
|
|
8928
|
-
options?.textTypeFacts,
|
|
8929
|
-
);
|
|
9529
|
+
: signalAst;
|
|
9530
|
+
const textTypedAst = hasDomBindings
|
|
9531
|
+
? memoizedAst
|
|
9532
|
+
: applyStringChildProofs(memoizedAst, source, cleanFilename, options?.textTypeFacts);
|
|
8930
9533
|
let constantAst = textTypedAst;
|
|
8931
9534
|
if (typeof options?.resolveCssModuleConstant === 'function') {
|
|
8932
9535
|
const constants = applyCssModuleConstants(
|
|
@@ -8960,6 +9563,7 @@ function compileAuthored(source, filename, options, bundlerMetadata) {
|
|
|
8960
9563
|
if (strongAnalysis?.diagnostics.length > 0) {
|
|
8961
9564
|
result.diagnostics = [...strongAnalysis.diagnostics, ...(result.diagnostics ?? [])];
|
|
8962
9565
|
}
|
|
9566
|
+
if (bindingConstants !== undefined) result.bindingConstants = bindingConstants;
|
|
8963
9567
|
return result;
|
|
8964
9568
|
}
|
|
8965
9569
|
|
|
@@ -9051,6 +9655,7 @@ function compileInternal(
|
|
|
9051
9655
|
).diagnostics;
|
|
9052
9656
|
if (bundlerMetadata !== null) {
|
|
9053
9657
|
bundlerMetadata.hydrateAst = hydratePreparation.ast;
|
|
9658
|
+
bundlerMetadata.independentWidgets = hydratePreparation.independentWidgets ?? [];
|
|
9054
9659
|
}
|
|
9055
9660
|
const compiled = compileInternal(
|
|
9056
9661
|
source,
|
|
@@ -9400,6 +10005,8 @@ function compileInternal(
|
|
|
9400
10005
|
ownedCssInjections: new Set(),
|
|
9401
10006
|
componentOwners: [],
|
|
9402
10007
|
currentComponentOwner: null,
|
|
10008
|
+
signalBindingsUsed: ast._octaneSignalDeclarations === true,
|
|
10009
|
+
signalBindingsEager: ast._octaneSignalDeclarations === true,
|
|
9403
10010
|
currentComponentLocals: null, // Set<string> while compiling a component body; null otherwise
|
|
9404
10011
|
currentMapTemps: null, // expression temps (map receiver/method, folded condition) owned by this function
|
|
9405
10012
|
currentAutoMemoOffset: 0, // flat compiler-cache cell offset for the body being emitted
|
|
@@ -9930,18 +10537,18 @@ function compileInternal(
|
|
|
9930
10537
|
).filter((n) => n.type !== 'HeadHoist');
|
|
9931
10538
|
if (inheritSoleCompRoot(bodyNodes, ctx)) eligible = false;
|
|
9932
10539
|
}
|
|
9933
|
-
info.eligible = eligible;
|
|
10540
|
+
info.eligible = eligible && !compNode._octaneBindingView;
|
|
9934
10541
|
// Single-ELEMENT-root output: the component's body renders exactly one plain
|
|
9935
10542
|
// DOM element (not a component tag, fragment, or control-flow). Such a
|
|
9936
10543
|
// component self-delimits via that element on CLIENT mount — its
|
|
9937
10544
|
// `componentSlot` needs no `comp`/`/comp` markers (singleRoot path), exactly
|
|
9938
10545
|
// like a single-root `@for` item. (Output-shape based — independent of which
|
|
9939
10546
|
// hooks it calls.)
|
|
9940
|
-
info.singleRoot = singleHostComponentRoot(compNode);
|
|
10547
|
+
info.singleRoot = !compNode._octaneBindingView && singleHostComponentRoot(compNode);
|
|
9941
10548
|
// Transitive @if-arm form of the same proof — resolved by the fixed point
|
|
9942
10549
|
// below once every same-module callee an arm depends on is itself proven.
|
|
9943
10550
|
info.singleRootIfDeps =
|
|
9944
|
-
!info.singleRoot && isVoidJsxCodeBlockFunction(compNode)
|
|
10551
|
+
!compNode._octaneBindingView && !info.singleRoot && isVoidJsxCodeBlockFunction(compNode)
|
|
9945
10552
|
? collectSingleRootIfDeps(compNode.body.render, locals, ctx)
|
|
9946
10553
|
: null;
|
|
9947
10554
|
}
|
|
@@ -10501,6 +11108,22 @@ function compileInternal(
|
|
|
10501
11108
|
ctx.runtimeNeeded.add('__vtSeen');
|
|
10502
11109
|
vtHintNodes.push(inheritOriginLoc(b.stmt(b.call(rtAlias('__vtSeen'))), moduleOrigin));
|
|
10503
11110
|
}
|
|
11111
|
+
const signalBindingNodes = [];
|
|
11112
|
+
if (ctx.signalBindingsUsed) {
|
|
11113
|
+
ctx.runtimeNeeded.add('enableSignalBindings');
|
|
11114
|
+
signalBindingNodes.push(
|
|
11115
|
+
inheritOriginLoc(
|
|
11116
|
+
b.stmt(
|
|
11117
|
+
b.call(
|
|
11118
|
+
'_$enableSignalBindings',
|
|
11119
|
+
b.literal(1),
|
|
11120
|
+
...(ctx.signalBindingsEager ? [] : [b.literal(true)]),
|
|
11121
|
+
),
|
|
11122
|
+
),
|
|
11123
|
+
moduleOrigin,
|
|
11124
|
+
),
|
|
11125
|
+
);
|
|
11126
|
+
}
|
|
10504
11127
|
|
|
10505
11128
|
// All remaining authored memo sites have now received their real hook slots.
|
|
10506
11129
|
// Lower callable helpers, returned-JSX functions, explicit-slot calls and
|
|
@@ -10509,6 +11132,7 @@ function compileInternal(
|
|
|
10509
11132
|
// accidentally import DOM memo helpers for a universal renderer's scope.
|
|
10510
11133
|
let moduleBody = [
|
|
10511
11134
|
...nativeReadActivationNodes(ctx, moduleOrigin),
|
|
11135
|
+
...signalBindingNodes,
|
|
10512
11136
|
...vtHintNodes,
|
|
10513
11137
|
...delegateNodes,
|
|
10514
11138
|
...styleNodes,
|
|
@@ -10561,9 +11185,12 @@ function compileInternal(
|
|
|
10561
11185
|
};
|
|
10562
11186
|
const printed = printNodeWithMap(program, ctx);
|
|
10563
11187
|
|
|
10564
|
-
/** @type {{ code: string, map: any, diagnostics: any, inspect?: any, __universalSourceMapComposed?: boolean }} */
|
|
11188
|
+
/** @type {{ code: string, map: any, diagnostics: any, inspect?: any, streamedSignals?: true, __universalSourceMapComposed?: boolean }} */
|
|
10565
11189
|
const result = {
|
|
10566
11190
|
code: printed.code,
|
|
11191
|
+
...(ast._octaneSignalDeclarations === true || ctx.nativeReads
|
|
11192
|
+
? { streamedSignals: true }
|
|
11193
|
+
: null),
|
|
10567
11194
|
map: {
|
|
10568
11195
|
version: 3,
|
|
10569
11196
|
sources: [ctx.mapSourceName],
|
|
@@ -10684,6 +11311,8 @@ function compileServer(
|
|
|
10684
11311
|
nativeModuleStyles: options?.nativeReads === true && hasModuleStyleMaps(ast.body),
|
|
10685
11312
|
hmr: false, // SSR never hot-swaps in place; client/server production slot shapes stay aligned
|
|
10686
11313
|
dev: !!(options && options.dev),
|
|
11314
|
+
signalBindingsUsed: ast._octaneSignalDeclarations === true,
|
|
11315
|
+
signalBindingsEager: ast._octaneSignalDeclarations === true || options?.nativeReads === true,
|
|
10687
11316
|
// SSR MIRROR of the parallel-`use()` pipeline (docs/suspense-parallel-use-
|
|
10688
11317
|
// plan.md Phase 5): the same memoize (Pass A) + hoist/batch (Pass B)
|
|
10689
11318
|
// transforms run on server bodies, emitting `_$puMemo`/`_$puBatch` — the
|
|
@@ -10862,12 +11491,34 @@ function compileServer(
|
|
|
10862
11491
|
// the flush may register `hookSlots` as a needed runtime import.
|
|
10863
11492
|
flushTailHookSymbols(ctx);
|
|
10864
11493
|
const nativeActivation = nativeReadActivationNodes(ctx, ctx._moduleOrigin);
|
|
11494
|
+
const signalActivation = [];
|
|
11495
|
+
if (ctx.signalBindingsUsed || ctx.signalBindingsEager) {
|
|
11496
|
+
ctx.runtimeNeeded.add('enableServerSignalBindings');
|
|
11497
|
+
signalActivation.push(
|
|
11498
|
+
inheritOriginLoc(
|
|
11499
|
+
b.stmt(
|
|
11500
|
+
b.call(
|
|
11501
|
+
'_$enableServerSignalBindings',
|
|
11502
|
+
b.literal(1),
|
|
11503
|
+
...(ctx.signalBindingsEager ? [] : [b.literal(true)]),
|
|
11504
|
+
),
|
|
11505
|
+
),
|
|
11506
|
+
ctx._moduleOrigin,
|
|
11507
|
+
),
|
|
11508
|
+
);
|
|
11509
|
+
}
|
|
10865
11510
|
const runtimeImportNodes = buildRuntimeImportNodes(ctx, 'octane/server', ctx._moduleOrigin);
|
|
10866
11511
|
const helperNodes = hoistedHelperNodes(ctx);
|
|
10867
11512
|
const program = {
|
|
10868
11513
|
type: 'Program',
|
|
10869
11514
|
sourceType: 'module',
|
|
10870
|
-
body: [
|
|
11515
|
+
body: [
|
|
11516
|
+
...runtimeImportNodes,
|
|
11517
|
+
...nativeActivation,
|
|
11518
|
+
...signalActivation,
|
|
11519
|
+
...helperNodes,
|
|
11520
|
+
...bodyNodes,
|
|
11521
|
+
],
|
|
10871
11522
|
metadata: { path: [] },
|
|
10872
11523
|
start: ast.start,
|
|
10873
11524
|
end: ast.end,
|
|
@@ -10876,6 +11527,9 @@ function compileServer(
|
|
|
10876
11527
|
const printed = printNodeWithMap(program, ctx);
|
|
10877
11528
|
const result = {
|
|
10878
11529
|
code: printed.code,
|
|
11530
|
+
...(ast._octaneSignalDeclarations === true || ctx.nativeReads
|
|
11531
|
+
? { streamedSignals: true }
|
|
11532
|
+
: null),
|
|
10879
11533
|
map: {
|
|
10880
11534
|
version: 3,
|
|
10881
11535
|
sources: [ctx.mapSourceName],
|
|
@@ -10998,6 +11652,20 @@ function compileServerComponent(node, ctx) {
|
|
|
10998
11652
|
if (componentReferencedAboveDeclaration(ctx, node, name)) {
|
|
10999
11653
|
const declaration = isDefault ? b.export_default(fn) : isExported ? b.export(fn) : fn;
|
|
11000
11654
|
const nodes = [inheritOriginLoc(declaration, node)];
|
|
11655
|
+
if (node._octaneBindingView) {
|
|
11656
|
+
nodes.push(
|
|
11657
|
+
inheritOriginLoc(
|
|
11658
|
+
b.stmt(
|
|
11659
|
+
b.call(
|
|
11660
|
+
requireRuntimeForContext(ctx, 'bindPresentationView'),
|
|
11661
|
+
b.id(name),
|
|
11662
|
+
b.literal(node._octaneBindingView.id),
|
|
11663
|
+
),
|
|
11664
|
+
),
|
|
11665
|
+
node,
|
|
11666
|
+
),
|
|
11667
|
+
);
|
|
11668
|
+
}
|
|
11001
11669
|
if (warmNode !== null) {
|
|
11002
11670
|
nodes.push(
|
|
11003
11671
|
inheritOriginLoc(
|
|
@@ -11015,7 +11683,19 @@ function compileServerComponent(node, ctx) {
|
|
|
11015
11683
|
return nodes;
|
|
11016
11684
|
}
|
|
11017
11685
|
|
|
11018
|
-
|
|
11686
|
+
let initializer = functionExpressionFromDeclaration(fn, node);
|
|
11687
|
+
if (node._octaneBindingView) {
|
|
11688
|
+
initializer = markPure(
|
|
11689
|
+
inheritOriginLoc(
|
|
11690
|
+
b.call(
|
|
11691
|
+
requireRuntimeForContext(ctx, 'bindPresentationView'),
|
|
11692
|
+
initializer,
|
|
11693
|
+
b.literal(node._octaneBindingView.id),
|
|
11694
|
+
),
|
|
11695
|
+
node,
|
|
11696
|
+
),
|
|
11697
|
+
);
|
|
11698
|
+
}
|
|
11019
11699
|
const declaration = inheritOriginLoc(b.const(name, initializer), node);
|
|
11020
11700
|
const nodes = [
|
|
11021
11701
|
isExported && !isDefault ? inheritOriginLoc(b.export(declaration), node) : declaration,
|
|
@@ -11321,8 +12001,19 @@ function ssrCompileBodyWithMapTemps(
|
|
|
11321
12001
|
// needs no carrier. Other bodies can cross a component/value boundary, where
|
|
11322
12002
|
// the carrier distinguishes compiled HTML from authored text returns. This
|
|
11323
12003
|
// flag belongs to this body only; nested callbacks keep their own contract.
|
|
11324
|
-
|
|
11325
|
-
|
|
12004
|
+
const htmlHelper = node._octaneBindingView ? 'ssrBindingHtml' : 'ssrHtml';
|
|
12005
|
+
if (!unboxedHtml) ctx.runtimeNeeded.add(htmlHelper);
|
|
12006
|
+
body.push(
|
|
12007
|
+
b.return(
|
|
12008
|
+
unboxedHtml
|
|
12009
|
+
? htmlExpr
|
|
12010
|
+
: ssrCall(
|
|
12011
|
+
htmlHelper,
|
|
12012
|
+
[htmlExpr, ...(node._octaneBindingView ? [b.literal(node._octaneBindingView.id)] : [])],
|
|
12013
|
+
node,
|
|
12014
|
+
),
|
|
12015
|
+
),
|
|
12016
|
+
);
|
|
11326
12017
|
const origin =
|
|
11327
12018
|
node.loc != null
|
|
11328
12019
|
? node
|
|
@@ -11353,10 +12044,20 @@ function ssrCompileBodyWithMapTemps(
|
|
|
11353
12044
|
// flow — all serialize elements or `<!--[-->…<!--]-->` ranges
|
|
11354
12045
|
// that break text-node adjacency on their own)
|
|
11355
12046
|
function textAdjacencyKind(node, ctx) {
|
|
12047
|
+
if (
|
|
12048
|
+
node._octaneBindingText ||
|
|
12049
|
+
node._octaneBindingOpaque ||
|
|
12050
|
+
node._octaneBindingValue ||
|
|
12051
|
+
node._octaneBindingSlot
|
|
12052
|
+
)
|
|
12053
|
+
return 'other';
|
|
11356
12054
|
if (node.type !== 'Text') return 'other';
|
|
11357
12055
|
const lit = staticTextLiteral(node.expression);
|
|
11358
12056
|
if (lit !== null) return lit === '' ? 'empty' : 'static';
|
|
11359
|
-
return
|
|
12057
|
+
return isDirectSignalHandleExpression(node.expression) ||
|
|
12058
|
+
isKnownTextChildExpression(node.expression, ctx.knownStringChildLocals)
|
|
12059
|
+
? 'dyn'
|
|
12060
|
+
: 'other';
|
|
11360
12061
|
}
|
|
11361
12062
|
|
|
11362
12063
|
// Does the child at `i` have a text-producing sibling next to it (looking
|
|
@@ -11622,10 +12323,17 @@ function ssrEmitNode(
|
|
|
11622
12323
|
componentNs,
|
|
11623
12324
|
nlGuard = false,
|
|
11624
12325
|
) {
|
|
12326
|
+
if (node._octaneBindingValue || node._octaneBindingSlot)
|
|
12327
|
+
return ssrEmitTsrxExpression(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs);
|
|
11625
12328
|
switch (node.type) {
|
|
11626
12329
|
case 'Text': {
|
|
11627
12330
|
const expr = node.expression;
|
|
11628
|
-
if (
|
|
12331
|
+
if (
|
|
12332
|
+
!node._octaneBindingText &&
|
|
12333
|
+
expr &&
|
|
12334
|
+
expr.type === 'Literal' &&
|
|
12335
|
+
typeof expr.value === 'string'
|
|
12336
|
+
) {
|
|
11629
12337
|
if (ctx.dev && ctx._ssrNestingText === true) {
|
|
11630
12338
|
ctx.runtimeNeeded.add('ssrNestingText');
|
|
11631
12339
|
return ssrCall(
|
|
@@ -11663,30 +12371,44 @@ function ssrEmitNode(
|
|
|
11663
12371
|
// primitive to text) — mirrors Ripple's `{expr}` vs `{expr as string}`.
|
|
11664
12372
|
// rewriteHookCalls: a `use(thenable)` in this hole bypasses the setup
|
|
11665
12373
|
// rewrite, so key it here too (else it collides with sibling/nested use()).
|
|
11666
|
-
if (
|
|
12374
|
+
if (
|
|
12375
|
+
isDirectSignalHandleExpression(expr) ||
|
|
12376
|
+
isKnownTextChildExpression(expr, ctx.knownStringChildLocals)
|
|
12377
|
+
) {
|
|
11667
12378
|
// ssrTextPre = ssrText + the runtime leading-'\n' protection (the value
|
|
11668
12379
|
// isn't known at compile time here).
|
|
11669
12380
|
const fn =
|
|
11670
12381
|
ctx.dev && ctx._ssrNestingText === true
|
|
11671
12382
|
? 'ssrNestingText'
|
|
11672
|
-
: nlGuard
|
|
12383
|
+
: nlGuard && !node._octaneBindingText
|
|
11673
12384
|
? 'ssrTextPre'
|
|
11674
12385
|
: 'ssrText';
|
|
11675
12386
|
ctx.runtimeNeeded.add(fn);
|
|
11676
|
-
|
|
12387
|
+
const value = resolveStyleExpr(rewriteHookCalls(expr, ctx, name), cssHash);
|
|
12388
|
+
const text = ssrCall(fn, [ssrSignalValue(value, ctx, node, true)], node);
|
|
12389
|
+
return node._octaneBindingText
|
|
12390
|
+
? ssrBindingRange(text, node._octaneBindingText, 't', ctx, node)
|
|
12391
|
+
: text;
|
|
11677
12392
|
}
|
|
11678
12393
|
// rewriteJsxValues lowers any JSX embedded in the expression (e.g.
|
|
11679
12394
|
// `{cond && <div/>}`, a ternary, a `.map(x => <Row/>)`) to printable
|
|
11680
12395
|
// createElement(...) descriptors — exactly like ssrEmitTsrxExpression and
|
|
11681
12396
|
// the client makeChildCall. Without it the raw JSX leaks into the emitted
|
|
11682
12397
|
// ssrChild(...) call as unparseable source.
|
|
11683
|
-
const childHelper =
|
|
12398
|
+
const childHelper = node._octaneBindingOpaque
|
|
12399
|
+
? 'ssrBindingChild'
|
|
12400
|
+
: nlGuard
|
|
12401
|
+
? 'ssrChildPre'
|
|
12402
|
+
: 'ssrChild';
|
|
11684
12403
|
ctx.runtimeNeeded.add(childHelper);
|
|
11685
12404
|
const childExpr = ssrCall(
|
|
11686
12405
|
childHelper,
|
|
11687
12406
|
[
|
|
11688
12407
|
resolveStyleExpr(rewriteJsxValues(rewriteHookCalls(expr, ctx, name), ctx), cssHash),
|
|
11689
12408
|
b.id('__s'),
|
|
12409
|
+
...(node._octaneBindingOpaque
|
|
12410
|
+
? [b.literal(bindingMarker(node._octaneBindingOpaque, 'o'))]
|
|
12411
|
+
: []),
|
|
11690
12412
|
],
|
|
11691
12413
|
node,
|
|
11692
12414
|
);
|
|
@@ -11767,7 +12489,9 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
11767
12489
|
rejectVoidElementContent(tag, node, ctx);
|
|
11768
12490
|
rejectTextareaValueChildren(tag, node, ctx);
|
|
11769
12491
|
rejectDangerouslySetInnerHTMLChildren(tag, node, ctx);
|
|
11770
|
-
const attrs =
|
|
12492
|
+
const attrs = expandKnownAttributeSpreads(
|
|
12493
|
+
node.attributes || node.openingElement?.attributes || [],
|
|
12494
|
+
);
|
|
11771
12495
|
const devFormActionAttribute =
|
|
11772
12496
|
ctx.dev && (tag === 'form' || tag === 'button' || tag === 'input')
|
|
11773
12497
|
? attrs.find(
|
|
@@ -11867,8 +12591,35 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
11867
12591
|
else directAttributeIdentities.add(identity);
|
|
11868
12592
|
}
|
|
11869
12593
|
const firstSpreadIdx = attrs.findIndex(
|
|
11870
|
-
(a) => a.type === 'SpreadAttribute' || a.type === 'JSXSpreadAttribute',
|
|
12594
|
+
(a) => !a.knownSource && (a.type === 'SpreadAttribute' || a.type === 'JSXSpreadAttribute'),
|
|
11871
12595
|
);
|
|
12596
|
+
const hostSignalSite = directSignalSite(ctx, node, 'binding');
|
|
12597
|
+
let directSignalControlSite = null;
|
|
12598
|
+
const markDirectControl = (expression, origin) => {
|
|
12599
|
+
if (!canCarryDirectSignalHandle(expression)) return expression;
|
|
12600
|
+
const site = firstSpreadIdx === -1 ? directSignalSite(ctx, node, 'input') : hostSignalSite;
|
|
12601
|
+
if (directSignalControlSite === null) {
|
|
12602
|
+
if (directAttributeIdentities.has('data-octane-input')) {
|
|
12603
|
+
throw new Error(
|
|
12604
|
+
'`data-octane-input` is reserved for compiler-owned writable signal controls.',
|
|
12605
|
+
);
|
|
12606
|
+
}
|
|
12607
|
+
directSignalControlSite = site;
|
|
12608
|
+
bakeLit(` data-octane-input="${escapeAttr(site)}"`, 'data-octane-input', null, null);
|
|
12609
|
+
}
|
|
12610
|
+
ctx.signalBindingsUsed = true;
|
|
12611
|
+
ctx.runtimeNeeded.add('ssrSignalControlValue');
|
|
12612
|
+
return ssrCall('ssrSignalControlValue', [expression, b.literal(site)], origin);
|
|
12613
|
+
};
|
|
12614
|
+
if (firstSpreadIdx !== -1 && (tag === 'input' || tag === 'textarea' || tag === 'select')) {
|
|
12615
|
+
directSignalControlSite = hostSignalSite;
|
|
12616
|
+
if (directAttributeIdentities.has('data-octane-input')) {
|
|
12617
|
+
throw new Error(
|
|
12618
|
+
'`data-octane-input` is reserved for compiler-owned writable signal controls.',
|
|
12619
|
+
);
|
|
12620
|
+
}
|
|
12621
|
+
bakeLit(` data-octane-input="${escapeAttr(hostSignalSite)}"`, 'data-octane-input', null, null);
|
|
12622
|
+
}
|
|
11872
12623
|
// Customized built-ins suppress native unknown-property diagnostics based on
|
|
11873
12624
|
// the final `is` prop, regardless of where that prop appears in JSX. Resolve
|
|
11874
12625
|
// directly authored DEV hosts through the existing whole-element attribute
|
|
@@ -12023,13 +12774,40 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12023
12774
|
};
|
|
12024
12775
|
|
|
12025
12776
|
for (let attrI = 0; attrI < attrs.length; attrI++) {
|
|
12026
|
-
|
|
12777
|
+
let attr = attrs[attrI];
|
|
12778
|
+
if (attr.knownSource) {
|
|
12779
|
+
attr.knownSource.expression = bindAttributeEvaluation(
|
|
12780
|
+
tsrxExprNode(attr.argument, ctx, name, inlinedSubs),
|
|
12781
|
+
);
|
|
12782
|
+
continue;
|
|
12783
|
+
}
|
|
12784
|
+
if (attr.value?.type === 'JSXExpressionContainer') {
|
|
12785
|
+
const expression = resolveKnownAttributeSource(
|
|
12786
|
+
attr.value.expression,
|
|
12787
|
+
(source) => source.expression,
|
|
12788
|
+
);
|
|
12789
|
+
if (expression !== attr.value.expression)
|
|
12790
|
+
attr = { ...attr, value: { ...attr.value, expression } };
|
|
12791
|
+
}
|
|
12027
12792
|
if (attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') {
|
|
12028
12793
|
ensureAttrPart();
|
|
12794
|
+
ctx.signalBindingsUsed = true;
|
|
12029
12795
|
ctx.runtimeNeeded.add('ssrAttrs');
|
|
12030
12796
|
ctx.runtimeNeeded.add('ssrSnapshotSpread');
|
|
12031
12797
|
const tmp = bindAttributeEvaluation(
|
|
12032
|
-
ssrCall(
|
|
12798
|
+
ssrCall(
|
|
12799
|
+
'ssrSnapshotSpread',
|
|
12800
|
+
[
|
|
12801
|
+
tsrxExprNode(attr.argument, ctx, name, inlinedSubs),
|
|
12802
|
+
...(tag === 'input' || tag === 'textarea' || tag === 'select'
|
|
12803
|
+
? [b.literal(hostSignalSite)]
|
|
12804
|
+
: ctx.nativeReads
|
|
12805
|
+
? [undefinedNode()]
|
|
12806
|
+
: []),
|
|
12807
|
+
...(ctx.nativeReads ? [b.literal(true)] : []),
|
|
12808
|
+
],
|
|
12809
|
+
attr,
|
|
12810
|
+
),
|
|
12033
12811
|
);
|
|
12034
12812
|
if (resolveFormControlsAcrossSpreads) {
|
|
12035
12813
|
if (tag !== 'textarea' && formControlPart === -1) {
|
|
@@ -12072,6 +12850,29 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12072
12850
|
}
|
|
12073
12851
|
if (attr.type !== 'Attribute' && attr.type !== 'JSXAttribute') continue;
|
|
12074
12852
|
const rawAttrName = jsxAttrRawName(attr);
|
|
12853
|
+
if (attr._octaneBindingClassGroups) {
|
|
12854
|
+
const groups = attr._octaneBindingClassGroups;
|
|
12855
|
+
ctx.runtimeNeeded.add('ssrBindingClass');
|
|
12856
|
+
flush();
|
|
12857
|
+
parts.push(
|
|
12858
|
+
ssrCall(
|
|
12859
|
+
'ssrBindingClass',
|
|
12860
|
+
[
|
|
12861
|
+
b.literal(groups.receipt),
|
|
12862
|
+
bindAttributeEvaluation(
|
|
12863
|
+
tsrxExprNode(
|
|
12864
|
+
b.array([b.array(groups.baseline), b.array(groups.groups)]),
|
|
12865
|
+
ctx,
|
|
12866
|
+
name,
|
|
12867
|
+
inlinedSubs,
|
|
12868
|
+
),
|
|
12869
|
+
),
|
|
12870
|
+
],
|
|
12871
|
+
attr,
|
|
12872
|
+
),
|
|
12873
|
+
);
|
|
12874
|
+
continue;
|
|
12875
|
+
}
|
|
12075
12876
|
if (rawAttrName === 'key') {
|
|
12076
12877
|
bindDiscardedAttributeValue(attr.value);
|
|
12077
12878
|
continue;
|
|
@@ -12143,10 +12944,13 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12143
12944
|
const ctlInner =
|
|
12144
12945
|
val == null ? null : val.type === 'JSXExpressionContainer' ? val.expression : val;
|
|
12145
12946
|
if (tag === 'input') {
|
|
12146
|
-
|
|
12947
|
+
let ctlExpr =
|
|
12147
12948
|
ctlInner === null
|
|
12148
12949
|
? inheritOriginLoc(b.literal(true, 'true'), attr)
|
|
12149
12950
|
: tsrxExprNode(ctlInner, ctx, name, inlinedSubs);
|
|
12951
|
+
if (attrName === 'value' || attrName === 'checked') {
|
|
12952
|
+
ctlExpr = markDirectControl(ctlExpr, attr);
|
|
12953
|
+
}
|
|
12150
12954
|
if (formControlPart === -1) {
|
|
12151
12955
|
flush();
|
|
12152
12956
|
formControlPart = parts.length;
|
|
@@ -12161,11 +12965,12 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12161
12965
|
}
|
|
12162
12966
|
// textarea / select: value/defaultValue never serialize as attributes —
|
|
12163
12967
|
// captured for the content position (textarea) / projection scope (select).
|
|
12164
|
-
|
|
12968
|
+
let ctlExpr =
|
|
12165
12969
|
ctlInner === null
|
|
12166
12970
|
? inheritOriginLoc(b.literal(true, 'true'), attr)
|
|
12167
|
-
: tsrxExprNode(ctlInner, ctx, name, inlinedSubs)
|
|
12168
|
-
);
|
|
12971
|
+
: tsrxExprNode(ctlInner, ctx, name, inlinedSubs);
|
|
12972
|
+
if (attrName === 'value') ctlExpr = markDirectControl(ctlExpr, attr);
|
|
12973
|
+
ctlExpr = bindAttributeEvaluation(ctlExpr);
|
|
12169
12974
|
if (resolveFormControlsAcrossSpreads) {
|
|
12170
12975
|
if (tag === 'select' && formControlPart === -1) {
|
|
12171
12976
|
flush();
|
|
@@ -12174,6 +12979,9 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12174
12979
|
}
|
|
12175
12980
|
formControlSources.push(ssrDirectSource(ctx, attrName, ctlExpr, attr));
|
|
12176
12981
|
} else if (attrName === 'value') {
|
|
12982
|
+
if (directSignalControlSite !== null) {
|
|
12983
|
+
formControlSources.push(ssrDirectSource(ctx, attrName, ctlExpr, attr));
|
|
12984
|
+
}
|
|
12177
12985
|
ctlValue = ctlExpr;
|
|
12178
12986
|
ctlValueAttr = attr;
|
|
12179
12987
|
} else {
|
|
@@ -12297,6 +13105,8 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12297
13105
|
cssHash,
|
|
12298
13106
|
);
|
|
12299
13107
|
attrExpr = tsrxExprNode(attrInner, ctx, name, inlinedSubs);
|
|
13108
|
+
if (!(ctx.nativeReads && attrName === 'style'))
|
|
13109
|
+
attrExpr = ssrSignalValue(attrExpr, ctx, attr, true);
|
|
12300
13110
|
}
|
|
12301
13111
|
attrSources.push(
|
|
12302
13112
|
ssrDirectSource(
|
|
@@ -12338,6 +13148,13 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12338
13148
|
let inner = val.type === 'JSXExpressionContainer' ? val.expression : val;
|
|
12339
13149
|
|
|
12340
13150
|
if (attrName === 'style') {
|
|
13151
|
+
if (!ctx.nativeReads && isDirectSignalHandleExpression(inner)) {
|
|
13152
|
+
throw new Error(
|
|
13153
|
+
'Direct signal style objects cannot install property-targeted bindings. ' +
|
|
13154
|
+
'Use fixed style properties such as `style={{ color: color$ }}`, or use `.get()` ' +
|
|
13155
|
+
'for a one-way whole-style value.',
|
|
13156
|
+
);
|
|
13157
|
+
}
|
|
12341
13158
|
inner = resolveStyleExpr(inner, cssHash);
|
|
12342
13159
|
if (!isAfterSpread && inner.type === 'Literal' && typeof inner.value === 'string') {
|
|
12343
13160
|
bakeLit(` style="${escapeAttr(inner.value)}"`, attrName, attr.name, inner);
|
|
@@ -12356,15 +13173,11 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12356
13173
|
flush();
|
|
12357
13174
|
ctx.runtimeNeeded.add('ssrStyle');
|
|
12358
13175
|
registerAttrLoweringOrigin(ctx, attr.name, 'ssrStyle', null);
|
|
13176
|
+
inner = tsrxExprNode(inner, ctx, name, inlinedSubs);
|
|
12359
13177
|
if (ctx.nativeReads)
|
|
12360
13178
|
inner = b.call(requireRuntimeForContext(ctx, 'readNativeDomStyle'), inner);
|
|
12361
|
-
|
|
12362
|
-
|
|
12363
|
-
'ssrStyle',
|
|
12364
|
-
[bindAttributeEvaluation(tsrxExprNode(inner, ctx, name, inlinedSubs))],
|
|
12365
|
-
attr,
|
|
12366
|
-
),
|
|
12367
|
-
);
|
|
13179
|
+
else inner = ssrSignalStyleObject(inner, ctx, attr);
|
|
13180
|
+
parts.push(ssrCall('ssrStyle', [bindAttributeEvaluation(inner)], attr));
|
|
12368
13181
|
continue;
|
|
12369
13182
|
}
|
|
12370
13183
|
|
|
@@ -12446,7 +13259,9 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12446
13259
|
flush();
|
|
12447
13260
|
ctx.runtimeNeeded.add('ssrAttr');
|
|
12448
13261
|
registerAttrLoweringOrigin(ctx, attr.name, 'ssrAttr', attrName);
|
|
12449
|
-
const valueExpr = bindAttributeEvaluation(
|
|
13262
|
+
const valueExpr = bindAttributeEvaluation(
|
|
13263
|
+
ssrSignalValue(tsrxExprNode(inner, ctx, name, inlinedSubs), ctx, attr, true),
|
|
13264
|
+
);
|
|
12450
13265
|
if (
|
|
12451
13266
|
devFormActionSources !== null &&
|
|
12452
13267
|
(attrName === 'type' ||
|
|
@@ -12474,10 +13289,18 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12474
13289
|
);
|
|
12475
13290
|
parts.push(singleDirectAttribute);
|
|
12476
13291
|
}
|
|
13292
|
+
let signalControlAttrs = null;
|
|
13293
|
+
if (directSignalControlSite !== null) {
|
|
13294
|
+
ctx.runtimeNeeded.add('ssrSignalControlAttrs');
|
|
13295
|
+
signalControlAttrs = ssrCall('ssrSignalControlAttrs', [b.array(formControlSources)], node);
|
|
13296
|
+
}
|
|
12477
13297
|
if (formControlPart !== -1) {
|
|
12478
13298
|
if (tag === 'input') {
|
|
12479
13299
|
ctx.runtimeNeeded.add('ssrInputAttrs');
|
|
12480
|
-
|
|
13300
|
+
let inputAttrs = ssrCall('ssrInputAttrs', [b.array(formControlSources)], node);
|
|
13301
|
+
if (signalControlAttrs !== null) {
|
|
13302
|
+
inputAttrs = inheritOriginLoc(b.binary('+', inputAttrs, signalControlAttrs), node);
|
|
13303
|
+
}
|
|
12481
13304
|
// React serializes/coerces ordinary attributes before projecting the
|
|
12482
13305
|
// effective checked/value state. Expressions and spread getters have
|
|
12483
13306
|
// already run into temps in authored order; move only this serialization
|
|
@@ -12488,8 +13311,18 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12488
13311
|
parts.push(inputAttrs);
|
|
12489
13312
|
} else {
|
|
12490
13313
|
ctx.runtimeNeeded.add('ssrSelectAttrs');
|
|
12491
|
-
|
|
12492
|
-
|
|
13314
|
+
const selectAttrs = ssrCall('ssrSelectAttrs', [b.array(formControlSources)], node);
|
|
13315
|
+
parts[formControlPart] =
|
|
13316
|
+
signalControlAttrs === null
|
|
13317
|
+
? selectAttrs
|
|
13318
|
+
: inheritOriginLoc(b.binary('+', selectAttrs, signalControlAttrs), node);
|
|
13319
|
+
}
|
|
13320
|
+
} else if (signalControlAttrs !== null) {
|
|
13321
|
+
// Textarea values live in content; a direct select value lives in its
|
|
13322
|
+
// option scope. Neither needs a form-attribute slot, but both still need
|
|
13323
|
+
// the winning writable identity on the opening tag for early edits.
|
|
13324
|
+
flush();
|
|
13325
|
+
parts.push(signalControlAttrs);
|
|
12493
13326
|
}
|
|
12494
13327
|
if (attrPart !== -1) {
|
|
12495
13328
|
ctx.runtimeNeeded.add('ssrAttrs');
|
|
@@ -12589,6 +13422,9 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12589
13422
|
} else if (
|
|
12590
13423
|
htmlSources.length === 0 &&
|
|
12591
13424
|
onlyChild0 !== null &&
|
|
13425
|
+
!onlyChild0._octaneBindingOpaque &&
|
|
13426
|
+
!onlyChild0._octaneBindingValue &&
|
|
13427
|
+
!onlyChild0._octaneBindingSlot &&
|
|
12592
13428
|
staticTextLiteral(onlyChild0.expression) === null &&
|
|
12593
13429
|
!isKnownTextChildExpression(onlyChild0.expression, ctx.knownStringChildLocals)
|
|
12594
13430
|
) {
|
|
@@ -12808,11 +13644,12 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12808
13644
|
|
|
12809
13645
|
function ssrEmitComponent(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs) {
|
|
12810
13646
|
registerOriginAlias(ctx, node.closingElement?.name, node.id || node.openingElement?.name);
|
|
13647
|
+
const invocationSite = componentInvocationSite(ctx, node);
|
|
12811
13648
|
// M3 inherit-range: consume the body-root flag ONCE, before this component's
|
|
12812
13649
|
// props/children compile below (they recurse into ssrEmitNodes/ssrCompileSub
|
|
12813
13650
|
// and must not inherit it). Set by ssrCompileBody only for the sole
|
|
12814
13651
|
// comp-call root of a `@{}` body — which is exactly this emit.
|
|
12815
|
-
const inherit = ctx._ssrInheritRoot === true;
|
|
13652
|
+
const inherit = ctx._ssrInheritRoot === true && !node._octaneBindingSite;
|
|
12816
13653
|
ctx._ssrInheritRoot = false;
|
|
12817
13654
|
// Capture before compiling attributes/children: nested subs temporarily mutate
|
|
12818
13655
|
// this context flag. Only this component's immediate children sub inherits the
|
|
@@ -12967,11 +13804,11 @@ function ssrEmitComponent(node, ctx, name, inlinedSubs, parentNs, cssHash, compo
|
|
|
12967
13804
|
// The direct mode-only Activity was lowered to ActivityStatement. Richer
|
|
12968
13805
|
// Activity configs and generic explicit-key + spread sites keep one ordered
|
|
12969
13806
|
// element config, matching createElement without changing simple call sites.
|
|
12970
|
-
ctx.runtimeNeeded.add('
|
|
13807
|
+
ctx.runtimeNeeded.add('createElementFromConfig');
|
|
12971
13808
|
ctx.runtimeNeeded.add('ssrChild');
|
|
12972
13809
|
const descriptor = ssrCall(
|
|
12973
|
-
'
|
|
12974
|
-
[compExpr, inheritOriginLoc(b.object(propNodes), node)],
|
|
13810
|
+
'createElementFromConfig',
|
|
13811
|
+
[b.literal(invocationSite), compExpr, inheritOriginLoc(b.object(propNodes), node)],
|
|
12975
13812
|
node,
|
|
12976
13813
|
);
|
|
12977
13814
|
const child = ssrCall('ssrChild', [descriptor, b.id('__s')], node);
|
|
@@ -12991,16 +13828,38 @@ function ssrEmitComponent(node, ctx, name, inlinedSubs, parentNs, cssHash, compo
|
|
|
12991
13828
|
args.push(
|
|
12992
13829
|
b.literal(componentNs, JSON.stringify(componentNs)),
|
|
12993
13830
|
b.literal(inherit, inherit ? 'true' : 'false'),
|
|
13831
|
+
keyExpr ?? undefinedNode(),
|
|
13832
|
+
b.literal(invocationSite),
|
|
13833
|
+
);
|
|
13834
|
+
} else {
|
|
13835
|
+
args.push(
|
|
13836
|
+
b.literal(inherit, inherit ? 'true' : 'false'),
|
|
13837
|
+
keyExpr ?? undefinedNode(),
|
|
13838
|
+
undefinedNode(),
|
|
13839
|
+
b.literal(invocationSite),
|
|
12994
13840
|
);
|
|
12995
|
-
|
|
12996
|
-
|
|
12997
|
-
args.push(b.literal(
|
|
12998
|
-
} else if (inherit) {
|
|
12999
|
-
args.push(b.literal(true, 'true'));
|
|
13841
|
+
}
|
|
13842
|
+
if (node._octaneBindingSite) {
|
|
13843
|
+
args.push(b.literal(bindingMarker(node._octaneBindingSite, 'v')));
|
|
13000
13844
|
}
|
|
13001
13845
|
return ssrCall(helper, args, node);
|
|
13002
13846
|
}
|
|
13003
13847
|
|
|
13848
|
+
function bindingMarker(site, kind) {
|
|
13849
|
+
return '[b;' + site.id + ';' + site.site + ';' + kind;
|
|
13850
|
+
}
|
|
13851
|
+
|
|
13852
|
+
function bindingChildMarker(node) {
|
|
13853
|
+
return node._octaneBindingSlot
|
|
13854
|
+
? bindingMarker(node._octaneBindingSlot, 's')
|
|
13855
|
+
: bindingMarker(node._octaneBindingValue, 't');
|
|
13856
|
+
}
|
|
13857
|
+
|
|
13858
|
+
function ssrBindingRange(content, site, kind, ctx, origin) {
|
|
13859
|
+
ctx.runtimeNeeded.add('ssrBindingBlock');
|
|
13860
|
+
return ssrCall('ssrBindingBlock', [content, b.literal(bindingMarker(site, kind))], origin);
|
|
13861
|
+
}
|
|
13862
|
+
|
|
13004
13863
|
// ---------------------------------------------------------------------------
|
|
13005
13864
|
// Server control flow — @if/@for/@switch/@try lowered to HTML-string builders.
|
|
13006
13865
|
// Each branch/item/case body is compiled (via ssrCompileSub) into a server
|
|
@@ -13201,6 +14060,29 @@ function ssrEmitIf(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs)
|
|
|
13201
14060
|
node.alternate,
|
|
13202
14061
|
)
|
|
13203
14062
|
: ssrHtmlTemplate([], node, ctx);
|
|
14063
|
+
if (node._octaneBindingSite) {
|
|
14064
|
+
return ssrCall(
|
|
14065
|
+
'ssrControl',
|
|
14066
|
+
[
|
|
14067
|
+
b.literal(ssrControlKey('if', node)),
|
|
14068
|
+
ssrThunk(
|
|
14069
|
+
b.conditional(
|
|
14070
|
+
testExpr,
|
|
14071
|
+
ssrBindingRange(thenInner, node._octaneBindingSite, thenSub ? '0' : '-1', ctx, node),
|
|
14072
|
+
ssrBindingRange(
|
|
14073
|
+
elseInner,
|
|
14074
|
+
node._octaneBindingSite,
|
|
14075
|
+
node.alternate ? '1' : '-1',
|
|
14076
|
+
ctx,
|
|
14077
|
+
node,
|
|
14078
|
+
),
|
|
14079
|
+
),
|
|
14080
|
+
node,
|
|
14081
|
+
),
|
|
14082
|
+
],
|
|
14083
|
+
node,
|
|
14084
|
+
);
|
|
14085
|
+
}
|
|
13204
14086
|
return ssrCall(
|
|
13205
14087
|
'ssrBlock',
|
|
13206
14088
|
[
|
|
@@ -13266,6 +14148,7 @@ function ssrEmitActivity(node, ctx, name, inlinedSubs, parentNs, cssHash, compon
|
|
|
13266
14148
|
}
|
|
13267
14149
|
|
|
13268
14150
|
function ssrEmitFor(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs) {
|
|
14151
|
+
const bindingSite = node._octaneBindingSite;
|
|
13269
14152
|
// rewriteHookCalls: key any `use(thenable)` in the @for iterable expression.
|
|
13270
14153
|
const itemsExpr = rewriteHookCalls(node.right, ctx, name);
|
|
13271
14154
|
const mapCall = node.nativeArrayMap || null;
|
|
@@ -13369,22 +14252,37 @@ function ssrEmitFor(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs
|
|
|
13369
14252
|
node.index ? [b.id('__it'), b.id('__i')] : [b.id('__it')],
|
|
13370
14253
|
node,
|
|
13371
14254
|
);
|
|
13372
|
-
const sharedItemRange =
|
|
13373
|
-
|
|
13374
|
-
const
|
|
14255
|
+
const sharedItemRange =
|
|
14256
|
+
!bindingSite && (markerlessItem || canShareSsrComponentItemRange(node, ctx));
|
|
14257
|
+
const itemHtml =
|
|
14258
|
+
sharedItemRange || bindingSite ? itemCall : ssrCall('ssrBlock', [itemCall], node);
|
|
14259
|
+
let renderItem = itemNeedsIdentity
|
|
13375
14260
|
? ssrCall(
|
|
13376
14261
|
'ssrForItem',
|
|
13377
14262
|
[
|
|
13378
|
-
itemKey,
|
|
14263
|
+
bindingSite ? b.id('__bindingKeyValue') : itemKey,
|
|
13379
14264
|
b.id(itemSub.fnName),
|
|
13380
14265
|
b.id('__it'),
|
|
13381
14266
|
node.index ? b.id('__i') : ssrVoid(node),
|
|
13382
14267
|
b.id('__s'),
|
|
13383
|
-
b.literal(!sharedItemRange),
|
|
14268
|
+
b.literal(!sharedItemRange && !bindingSite),
|
|
14269
|
+
...(mapCall === null ? [] : [b.literal(true)]),
|
|
13384
14270
|
],
|
|
13385
14271
|
node,
|
|
13386
14272
|
)
|
|
13387
14273
|
: itemHtml;
|
|
14274
|
+
if (bindingSite) {
|
|
14275
|
+
ctx.runtimeNeeded.add('ssrBindingBlock');
|
|
14276
|
+
ctx.runtimeNeeded.add('ssrBindingKey');
|
|
14277
|
+
renderItem = ssrCall(
|
|
14278
|
+
'ssrBindingBlock',
|
|
14279
|
+
[
|
|
14280
|
+
renderItem,
|
|
14281
|
+
b.binary('+', b.literal(bindingMarker(bindingSite, 'k;')), b.id('__bindingKey')),
|
|
14282
|
+
],
|
|
14283
|
+
node,
|
|
14284
|
+
);
|
|
14285
|
+
}
|
|
13388
14286
|
if (node.empty) ctx.runtimeNeeded.add('ssrArm');
|
|
13389
14287
|
if (itemNeedsIdentity) ctx.runtimeNeeded.add('ssrForItem');
|
|
13390
14288
|
// Render every item into one incrementally-built string. Avoid map().join():
|
|
@@ -13398,8 +14296,30 @@ function ssrEmitFor(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs
|
|
|
13398
14296
|
const index = b.id('__i');
|
|
13399
14297
|
const loopBody = b.block([
|
|
13400
14298
|
b.const('__it', b.member(items, index, true)),
|
|
14299
|
+
...(bindingSite
|
|
14300
|
+
? [
|
|
14301
|
+
b.const('__bindingKeyValue', itemKey),
|
|
14302
|
+
b.const(
|
|
14303
|
+
'__bindingKey',
|
|
14304
|
+
ssrCall('ssrBindingKey', [b.id('__bindingKeyValue'), b.id('__bindingKeys')], node),
|
|
14305
|
+
),
|
|
14306
|
+
]
|
|
14307
|
+
: []),
|
|
13401
14308
|
b.stmt(b.assignment('+=', b.id('__html'), renderItem)),
|
|
13402
14309
|
]);
|
|
14310
|
+
const forRange = (content, hasItems) =>
|
|
14311
|
+
bindingSite
|
|
14312
|
+
? ssrCall(
|
|
14313
|
+
'ssrBindingBlock',
|
|
14314
|
+
[
|
|
14315
|
+
content,
|
|
14316
|
+
b.literal(
|
|
14317
|
+
'[f' + (hasItems ? '1' : '0') + ';b;' + bindingSite.id + ';' + bindingSite.site,
|
|
14318
|
+
),
|
|
14319
|
+
],
|
|
14320
|
+
node,
|
|
14321
|
+
)
|
|
14322
|
+
: ssrCall('ssrForBlock', [content, b.literal(hasItems)], node);
|
|
13403
14323
|
const render = b.arrow(
|
|
13404
14324
|
[],
|
|
13405
14325
|
b.block([
|
|
@@ -13411,10 +14331,11 @@ function ssrEmitFor(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs
|
|
|
13411
14331
|
),
|
|
13412
14332
|
b.if(
|
|
13413
14333
|
b.binary('===', b.member(items, 'length'), b.literal(0)),
|
|
13414
|
-
b.return(
|
|
14334
|
+
b.return(forRange(emptyCall, false)),
|
|
13415
14335
|
null,
|
|
13416
14336
|
),
|
|
13417
|
-
...(itemNeedsIdentity && keyDeclaration !== null ? [keyDeclaration] : []),
|
|
14337
|
+
...((itemNeedsIdentity || bindingSite) && keyDeclaration !== null ? [keyDeclaration] : []),
|
|
14338
|
+
...(bindingSite ? [b.const('__bindingKeys', b.new(b.id('Set'), node))] : []),
|
|
13418
14339
|
b.let('__html', b.literal('')),
|
|
13419
14340
|
b.for(
|
|
13420
14341
|
b.let('__i', b.literal(0)),
|
|
@@ -13422,7 +14343,7 @@ function ssrEmitFor(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs
|
|
|
13422
14343
|
b.update('++', index),
|
|
13423
14344
|
loopBody,
|
|
13424
14345
|
),
|
|
13425
|
-
b.return(
|
|
14346
|
+
b.return(forRange(b.id('__html'), true)),
|
|
13426
14347
|
]),
|
|
13427
14348
|
);
|
|
13428
14349
|
const fast = ssrCall('ssrControl', [b.literal(ssrControlKey('for', node)), render], node);
|
|
@@ -13632,6 +14553,33 @@ function ssrControlKey(kind, node) {
|
|
|
13632
14553
|
// renderer component so its template scopes and hydration range both survive.
|
|
13633
14554
|
function ssrEmitTsrxExpression(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs) {
|
|
13634
14555
|
const expr = node.expression;
|
|
14556
|
+
if (node._octaneBindingOpaque || node._octaneBindingValue || node._octaneBindingSlot) {
|
|
14557
|
+
ctx.runtimeNeeded.add('ssrBindingChild');
|
|
14558
|
+
const child = ssrCall(
|
|
14559
|
+
'ssrBindingChild',
|
|
14560
|
+
[
|
|
14561
|
+
resolveStyleExpr(rewriteJsxValues(rewriteHookCalls(expr, ctx, name), ctx), cssHash),
|
|
14562
|
+
b.id('__s'),
|
|
14563
|
+
b.literal(
|
|
14564
|
+
node._octaneBindingValue || node._octaneBindingSlot
|
|
14565
|
+
? bindingChildMarker(node)
|
|
14566
|
+
: bindingMarker(node._octaneBindingOpaque, 'o'),
|
|
14567
|
+
),
|
|
14568
|
+
],
|
|
14569
|
+
node,
|
|
14570
|
+
);
|
|
14571
|
+
if (componentNs === null) return child;
|
|
14572
|
+
ctx.runtimeNeeded.add('ssrInNamespace');
|
|
14573
|
+
return ssrCall('ssrInNamespace', [b.literal(componentNs), ssrThunk(child, node)], node);
|
|
14574
|
+
}
|
|
14575
|
+
if (isDirectSignalHandleExpression(expr)) {
|
|
14576
|
+
ctx.runtimeNeeded.add('ssrText');
|
|
14577
|
+
return ssrCall(
|
|
14578
|
+
'ssrText',
|
|
14579
|
+
[ssrSignalValue(resolveStyleExpr(rewriteHookCalls(expr, ctx, name), cssHash), ctx, node)],
|
|
14580
|
+
node,
|
|
14581
|
+
);
|
|
14582
|
+
}
|
|
13635
14583
|
if (node.returnedJsxValue === true && requiresTemplateNormalization(expr, 'html', true, ctx)) {
|
|
13636
14584
|
// A returned JSX value that contains template-only syntax is one compiled
|
|
13637
14585
|
// renderer on the client (rather than a descriptor array whose value
|
|
@@ -13656,7 +14604,19 @@ function ssrEmitTsrxExpression(node, ctx, name, inlinedSubs, parentNs, cssHash,
|
|
|
13656
14604
|
);
|
|
13657
14605
|
inlinedSubs.push(sub.fn);
|
|
13658
14606
|
ctx.runtimeNeeded.add('ssrComponent');
|
|
13659
|
-
return ssrCall(
|
|
14607
|
+
return ssrCall(
|
|
14608
|
+
'ssrComponent',
|
|
14609
|
+
[
|
|
14610
|
+
b.id('__s'),
|
|
14611
|
+
b.id(sub.fnName),
|
|
14612
|
+
b.object([]),
|
|
14613
|
+
b.literal(false),
|
|
14614
|
+
undefinedNode(),
|
|
14615
|
+
undefinedNode(),
|
|
14616
|
+
b.literal(componentInvocationSite(ctx, node)),
|
|
14617
|
+
],
|
|
14618
|
+
node,
|
|
14619
|
+
);
|
|
13660
14620
|
}
|
|
13661
14621
|
if (
|
|
13662
14622
|
expr &&
|
|
@@ -13686,10 +14646,7 @@ function ssrEmitTsrxExpression(node, ctx, name, inlinedSubs, parentNs, cssHash,
|
|
|
13686
14646
|
// position — body/arm/fragment roots and component-children roots,
|
|
13687
14647
|
// where a rich hole rides the de-opt value path because a portal there
|
|
13688
14648
|
// has no host to stamp (`_ssrHostChildPos`).
|
|
13689
|
-
const asIf =
|
|
13690
|
-
...b.if(expr.test, wrapAsBlockStmt(expr.consequent), wrapAsBlockStmt(expr.alternate)),
|
|
13691
|
-
loc: expr.loc, // same devLoc/control-key position as the client's claim
|
|
13692
|
-
};
|
|
14649
|
+
const asIf = conditionalJsxStatement(expr);
|
|
13693
14650
|
return ssrEmitIf(asIf, ctx, name, inlinedSubs, parentNs, cssHash, componentNs);
|
|
13694
14651
|
}
|
|
13695
14652
|
ctx.runtimeNeeded.add('ssrChild');
|
|
@@ -14162,6 +15119,17 @@ function compileComponent(node, ctx, options) {
|
|
|
14162
15119
|
// back to a full reload — the capture predates the wrapper).
|
|
14163
15120
|
statements.push(guarded(b.assignment('=', b.id(name), b.call('_$hmr', b.id(name)))));
|
|
14164
15121
|
}
|
|
15122
|
+
if (node._octaneBindingView) {
|
|
15123
|
+
statements.push(
|
|
15124
|
+
guarded(
|
|
15125
|
+
b.call(
|
|
15126
|
+
requireRuntimeForContext(ctx, 'bindPresentationView'),
|
|
15127
|
+
b.id(name),
|
|
15128
|
+
b.literal(node._octaneBindingView.id),
|
|
15129
|
+
),
|
|
15130
|
+
),
|
|
15131
|
+
);
|
|
15132
|
+
}
|
|
14165
15133
|
return { nodes: statements };
|
|
14166
15134
|
}
|
|
14167
15135
|
|
|
@@ -14191,6 +15159,18 @@ function compileComponent(node, ctx, options) {
|
|
|
14191
15159
|
// its fresh body to the previous canonical wrapper and re-export that same
|
|
14192
15160
|
// identity. Production components retain their tree-shakeable const binding.
|
|
14193
15161
|
let valueExpr = hmrWrap && isExported ? inheritOriginLoc(b.call('_$hmr', abiFn), node) : abiFn;
|
|
15162
|
+
if (node._octaneBindingView) {
|
|
15163
|
+
valueExpr = markPure(
|
|
15164
|
+
inheritOriginLoc(
|
|
15165
|
+
b.call(
|
|
15166
|
+
requireRuntimeForContext(ctx, 'bindPresentationView'),
|
|
15167
|
+
valueExpr,
|
|
15168
|
+
b.literal(node._octaneBindingView.id),
|
|
15169
|
+
),
|
|
15170
|
+
node,
|
|
15171
|
+
),
|
|
15172
|
+
);
|
|
15173
|
+
}
|
|
14194
15174
|
if (!hmrWrap && componentInfo?.singleRoot === true) {
|
|
14195
15175
|
valueExpr = inheritOriginLoc(singleRootInitializer(ctx, valueExpr), node);
|
|
14196
15176
|
componentInfo.singleRootInitialized = true;
|
|
@@ -14221,18 +15201,204 @@ function compileComponent(node, ctx, options) {
|
|
|
14221
15201
|
return { nodes: [declNode] };
|
|
14222
15202
|
}
|
|
14223
15203
|
|
|
15204
|
+
/** Gate explicitly eligible presentation views; ordinary codegen stays direct. */
|
|
15205
|
+
function preparePresentationHydration(body, node, ctx) {
|
|
15206
|
+
const proof = node._octanePresentationHydration;
|
|
15207
|
+
if (!proof || ctx.mode === 'server' || ctx._universalRuntimeUnit != null) return body;
|
|
15208
|
+
const writers = new Set([
|
|
15209
|
+
'bindSignalAttribute',
|
|
15210
|
+
'setEventHandler',
|
|
15211
|
+
'setAttribute',
|
|
15212
|
+
'setPlainAttribute',
|
|
15213
|
+
'setURLAttribute',
|
|
15214
|
+
'setStringData',
|
|
15215
|
+
'setBooleanAttribute',
|
|
15216
|
+
'setAriaAttribute',
|
|
15217
|
+
'setClassName',
|
|
15218
|
+
'setClassAttr',
|
|
15219
|
+
'updateFreshClassName',
|
|
15220
|
+
'updateFreshClassAttr',
|
|
15221
|
+
'setBindingClass',
|
|
15222
|
+
'setStyle',
|
|
15223
|
+
'setStyleProperty',
|
|
15224
|
+
'setStyleProperties',
|
|
15225
|
+
'nativeStyleBinding',
|
|
15226
|
+
'nativeProjectionBinding',
|
|
15227
|
+
]);
|
|
15228
|
+
if (proof.structural) {
|
|
15229
|
+
writers.add('setText');
|
|
15230
|
+
writers.add('bindSignalText');
|
|
15231
|
+
writers.add('markDangerouslySetInnerHTMLChildren');
|
|
15232
|
+
}
|
|
15233
|
+
for (const name of [...writers]) {
|
|
15234
|
+
if (name.startsWith('set')) writers.add(`${name}IfChanged`);
|
|
15235
|
+
}
|
|
15236
|
+
const preparation = new Set([
|
|
15237
|
+
'clone',
|
|
15238
|
+
'devHtmlNesting',
|
|
15239
|
+
'devEventListener',
|
|
15240
|
+
'queueRefAttach',
|
|
15241
|
+
'replaceRef',
|
|
15242
|
+
'queueOwnRefDetach',
|
|
15243
|
+
'readNativeDomStyle',
|
|
15244
|
+
'readNativeDomProps',
|
|
15245
|
+
]);
|
|
15246
|
+
if (proof.structural) {
|
|
15247
|
+
preparation.add('snapshotSpread');
|
|
15248
|
+
preparation.add('child');
|
|
15249
|
+
preparation.add('sibling');
|
|
15250
|
+
preparation.add('compilerMemoRegion');
|
|
15251
|
+
preparation.add('hookMemoEqual');
|
|
15252
|
+
preparation.add('beginNativeReadWitness');
|
|
15253
|
+
preparation.add('finishNativeReadWitness');
|
|
15254
|
+
preparation.add('replayNativeReadWitness');
|
|
15255
|
+
preparation.add('validateNativeReadWitness');
|
|
15256
|
+
preparation.add('drainFrag');
|
|
15257
|
+
// Registration stores the compiler-owned plan; it does not invoke it or write DOM.
|
|
15258
|
+
preparation.add('registerWarmPlan');
|
|
15259
|
+
preparation.add('presentationStructure');
|
|
15260
|
+
preparation.add('bindingText');
|
|
15261
|
+
preparation.add('bindingChildSlot');
|
|
15262
|
+
preparation.add('markBindingChildren');
|
|
15263
|
+
preparation.add('markChildrenBlock');
|
|
15264
|
+
}
|
|
15265
|
+
const helpers = new Map(
|
|
15266
|
+
[...ctx.runtimeNeeded].map((name) => [runtimeAliasForContext(ctx, name), name]),
|
|
15267
|
+
);
|
|
15268
|
+
let supported = proof.supported || proof.conditionalRest === true;
|
|
15269
|
+
const hostProofs = new Map();
|
|
15270
|
+
// Visit generated statements, but not authored callbacks or cleanup bodies.
|
|
15271
|
+
// New DOM helpers fail closed until their preparation contract is audited.
|
|
15272
|
+
const visit = (value, rewrite) => {
|
|
15273
|
+
if (!value || typeof value !== 'object') return value;
|
|
15274
|
+
if (Array.isArray(value)) return value.map((item) => visit(item, rewrite));
|
|
15275
|
+
if (!value.type || /Function/.test(value.type)) return value;
|
|
15276
|
+
if (value.type === 'ReturnStatement' && !value._octanePendingOutputGuard) supported = false;
|
|
15277
|
+
const helper =
|
|
15278
|
+
value.type === 'CallExpression' && value.callee.type === 'Identifier'
|
|
15279
|
+
? helpers.get(value.callee.name)
|
|
15280
|
+
: undefined;
|
|
15281
|
+
const host =
|
|
15282
|
+
helper === 'bindSignalHostPropSources' &&
|
|
15283
|
+
proof.structural &&
|
|
15284
|
+
value._octanePresentationHostSources?.length > 0;
|
|
15285
|
+
if (
|
|
15286
|
+
helper &&
|
|
15287
|
+
!host &&
|
|
15288
|
+
!writers.has(helper) &&
|
|
15289
|
+
!preparation.has(helper) &&
|
|
15290
|
+
!/^bag(?:\d+|Of)$/.test(helper)
|
|
15291
|
+
)
|
|
15292
|
+
supported = false;
|
|
15293
|
+
const result = { ...value };
|
|
15294
|
+
for (const key of Object.keys(value)) {
|
|
15295
|
+
if (key !== 'loc' && key !== 'metadata') result[key] = visit(value[key], rewrite);
|
|
15296
|
+
}
|
|
15297
|
+
if (rewrite && host) {
|
|
15298
|
+
const records = value._octanePresentationHostSources;
|
|
15299
|
+
const key = JSON.stringify(records);
|
|
15300
|
+
let receipt = hostProofs.get(key);
|
|
15301
|
+
if (receipt === undefined) {
|
|
15302
|
+
receipt = b.id(allocCompilerName(ctx, '__presentationHostSources'));
|
|
15303
|
+
hostProofs.set(key, receipt);
|
|
15304
|
+
ctx.hoistedHelpers.push(
|
|
15305
|
+
inheritOriginLoc(
|
|
15306
|
+
b.const(
|
|
15307
|
+
receipt,
|
|
15308
|
+
b.array(
|
|
15309
|
+
records.map((record) =>
|
|
15310
|
+
b.object(
|
|
15311
|
+
Object.entries(record).map(([name, field]) =>
|
|
15312
|
+
b.prop('init', b.id(name), b.literal(field)),
|
|
15313
|
+
),
|
|
15314
|
+
),
|
|
15315
|
+
),
|
|
15316
|
+
),
|
|
15317
|
+
),
|
|
15318
|
+
value,
|
|
15319
|
+
),
|
|
15320
|
+
);
|
|
15321
|
+
}
|
|
15322
|
+
const args = result.arguments.slice();
|
|
15323
|
+
while (args.length < 7) args.push(undefinedNode());
|
|
15324
|
+
return inheritOriginLoc(
|
|
15325
|
+
b.call(
|
|
15326
|
+
requireRuntimeForContext(ctx, 'presentationHostWrite'),
|
|
15327
|
+
result.callee,
|
|
15328
|
+
...args,
|
|
15329
|
+
receipt,
|
|
15330
|
+
),
|
|
15331
|
+
value,
|
|
15332
|
+
);
|
|
15333
|
+
}
|
|
15334
|
+
return rewrite && writers.has(helper)
|
|
15335
|
+
? inheritOriginLoc(
|
|
15336
|
+
b.call(
|
|
15337
|
+
requireRuntimeForContext(ctx, 'presentationWrite'),
|
|
15338
|
+
result.callee,
|
|
15339
|
+
b.literal(helper),
|
|
15340
|
+
...result.arguments,
|
|
15341
|
+
),
|
|
15342
|
+
value,
|
|
15343
|
+
)
|
|
15344
|
+
: result;
|
|
15345
|
+
};
|
|
15346
|
+
visit(body, false);
|
|
15347
|
+
const frame = b.id(allocCompilerName(ctx, '__presentationHydration'));
|
|
15348
|
+
const completed = b.id(allocCompilerName(ctx, '__presentationComplete'));
|
|
15349
|
+
const failure = proof.structural ? b.id(allocCompilerName(ctx, '__presentationFailure')) : null;
|
|
15350
|
+
const statements = supported ? visit(body, true) : body;
|
|
15351
|
+
let directiveEnd = 0;
|
|
15352
|
+
while (
|
|
15353
|
+
statements[directiveEnd]?.type === 'ExpressionStatement' &&
|
|
15354
|
+
typeof statements[directiveEnd].expression?.value === 'string'
|
|
15355
|
+
)
|
|
15356
|
+
directiveEnd++;
|
|
15357
|
+
const begin = b.call(
|
|
15358
|
+
requireRuntimeForContext(ctx, 'beginPresentationHydration'),
|
|
15359
|
+
b.id('__s'),
|
|
15360
|
+
b.literal(proof.id),
|
|
15361
|
+
b.literal(supported),
|
|
15362
|
+
...(proof.structural ? [b.literal(true)] : []),
|
|
15363
|
+
...(proof.conditionalRest ? [b.literal(true)] : []),
|
|
15364
|
+
);
|
|
15365
|
+
// A new, unsupported writer can still render normally, but must reject an
|
|
15366
|
+
// attempted lease before publishing anything. It never opens a frame.
|
|
15367
|
+
if (!supported)
|
|
15368
|
+
return [...statements.slice(0, directiveEnd), b.stmt(begin), ...statements.slice(directiveEnd)];
|
|
15369
|
+
return [
|
|
15370
|
+
...statements.slice(0, directiveEnd),
|
|
15371
|
+
b.const(frame, begin),
|
|
15372
|
+
b.let(completed, b.literal(false)),
|
|
15373
|
+
b.try(
|
|
15374
|
+
b.block([
|
|
15375
|
+
...statements.slice(directiveEnd),
|
|
15376
|
+
b.stmt(b.assignment('=', completed, b.literal(true))),
|
|
15377
|
+
]),
|
|
15378
|
+
failure === null
|
|
15379
|
+
? null
|
|
15380
|
+
: b.catch_clause(
|
|
15381
|
+
failure,
|
|
15382
|
+
null,
|
|
15383
|
+
b.block([
|
|
15384
|
+
b.stmt(b.call(requireRuntimeForContext(ctx, 'presentationFailure'), failure)),
|
|
15385
|
+
]),
|
|
15386
|
+
),
|
|
15387
|
+
b.block([
|
|
15388
|
+
b.stmt(b.call(requireRuntimeForContext(ctx, 'endPresentationHydration'), frame, completed)),
|
|
15389
|
+
]),
|
|
15390
|
+
),
|
|
15391
|
+
];
|
|
15392
|
+
}
|
|
15393
|
+
|
|
14224
15394
|
/**
|
|
14225
|
-
* Generate
|
|
14226
|
-
*
|
|
14227
|
-
*
|
|
14228
|
-
* `parentNs` is the namespace this body's JSX is rendered into. Top-level
|
|
14229
|
-
* components use 'opaque' because their call site can select HTML, SVG, or
|
|
14230
|
-
* MathML; an if/for/try body inherits its known host namespace.
|
|
14231
|
-
*
|
|
14232
|
-
* `cssHash` is the enclosing component's scoped-style hash (or null) — used to
|
|
14233
|
-
* resolve `{style ('cls')}` expressions to "<hash> cls" strings.
|
|
15395
|
+
* Generate the function AST for a top-level component or inlined item body.
|
|
15396
|
+
* `parentNs` is opaque for top-level components and inherited by directive
|
|
15397
|
+
* bodies. `cssHash` selects the enclosing scoped-style expression fallback.
|
|
14234
15398
|
*/
|
|
14235
15399
|
function compileFunctionBody(node, ctx, name, parentNs = 'html', cssHash = null, options = null) {
|
|
15400
|
+
const previousPresentationHydration = ctx.presentationHydration;
|
|
15401
|
+
ctx.presentationHydration = node._octanePresentationHydration;
|
|
14236
15402
|
const returnedOutput = options?.returnedOutput === true;
|
|
14237
15403
|
const previousAutoCalculatedRenderableRefs = ctx.currentAutoCalculatedRenderableRefs;
|
|
14238
15404
|
let autoCalculatedDeclarations = null;
|
|
@@ -14712,7 +15878,7 @@ function compileFunctionBody(node, ctx, name, parentNs = 'html', cssHash = null,
|
|
|
14712
15878
|
b.member(b.member(b.id('__s'), 'block'), 'pending'),
|
|
14713
15879
|
b.unary('!', b.member(b.member(b.id('__s'), 'block'), 'crossRenderUpdate')),
|
|
14714
15880
|
),
|
|
14715
|
-
b.return(null),
|
|
15881
|
+
{ ...b.return(null), _octanePendingOutputGuard: true },
|
|
14716
15882
|
null,
|
|
14717
15883
|
),
|
|
14718
15884
|
node,
|
|
@@ -14835,13 +16001,15 @@ function compileFunctionBody(node, ctx, name, parentNs = 'html', cssHash = null,
|
|
|
14835
16001
|
ctx.currentMapTemps = prevMapTemps;
|
|
14836
16002
|
// ONE FunctionDeclaration node — the caller prints it (once, with the full
|
|
14837
16003
|
// esrap map for top-level components) or embeds it in an enclosing body.
|
|
16004
|
+
const presentationBody = preparePresentationHydration(bodyStatements, node, ctx);
|
|
16005
|
+
ctx.presentationHydration = previousPresentationHydration;
|
|
14838
16006
|
const emittedFunction = b.function_declaration(
|
|
14839
16007
|
b.id(name, node.id ?? node),
|
|
14840
16008
|
fnParams,
|
|
14841
16009
|
b.block(
|
|
14842
16010
|
ctx.nativeReads
|
|
14843
|
-
? wrapNativeReadScope(
|
|
14844
|
-
:
|
|
16011
|
+
? wrapNativeReadScope(presentationBody, b.id('__s'), nativeReadNames(ctx))
|
|
16012
|
+
: presentationBody,
|
|
14845
16013
|
),
|
|
14846
16014
|
);
|
|
14847
16015
|
return inheritOriginLoc(
|
|
@@ -18285,10 +19453,29 @@ function compileReturnJsxFunction(node, ctx, options) {
|
|
|
18285
19453
|
// export follow-up statements — compile() embeds them in the single module
|
|
18286
19454
|
// AST, whose one esrap print yields the whole module map (chained consumers
|
|
18287
19455
|
// like @octanejs/mdx's two-stage .mdx map read segments off that print).
|
|
19456
|
+
const bindingStamp = node._octaneBindingView
|
|
19457
|
+
? [
|
|
19458
|
+
inheritOriginLoc(
|
|
19459
|
+
b.stmt(
|
|
19460
|
+
b.logical(
|
|
19461
|
+
'&&',
|
|
19462
|
+
b.binary('===', b.unary('typeof', b.id(name)), b.literal('function')),
|
|
19463
|
+
b.call(
|
|
19464
|
+
requireRuntimeForContext(ctx, 'bindPresentationView'),
|
|
19465
|
+
b.id(name),
|
|
19466
|
+
b.literal(node._octaneBindingView.id),
|
|
19467
|
+
),
|
|
19468
|
+
),
|
|
19469
|
+
),
|
|
19470
|
+
node,
|
|
19471
|
+
),
|
|
19472
|
+
]
|
|
19473
|
+
: [];
|
|
18288
19474
|
if (options && options.hmrWrap) {
|
|
18289
19475
|
const nodes = [
|
|
18290
19476
|
fn,
|
|
18291
19477
|
inheritOriginLoc(b.stmt(b.assignment('=', b.id(name), b.call('_$hmr', b.id(name)))), node),
|
|
19478
|
+
...bindingStamp,
|
|
18292
19479
|
];
|
|
18293
19480
|
if (options.default) {
|
|
18294
19481
|
nodes.push(
|
|
@@ -18305,10 +19492,11 @@ function compileReturnJsxFunction(node, ctx, options) {
|
|
|
18305
19492
|
return { nodes };
|
|
18306
19493
|
}
|
|
18307
19494
|
if (options && options.default) {
|
|
18308
|
-
return { nodes: [fn, inheritOriginLoc(b.export_default(b.id(name)), node)] };
|
|
19495
|
+
return { nodes: [fn, ...bindingStamp, inheritOriginLoc(b.export_default(b.id(name)), node)] };
|
|
18309
19496
|
}
|
|
18310
|
-
if (options && options.export)
|
|
18311
|
-
|
|
19497
|
+
if (options && options.export)
|
|
19498
|
+
return { nodes: [inheritOriginLoc(b.export(fn), node), ...bindingStamp] };
|
|
19499
|
+
return { nodes: [fn, ...bindingStamp] };
|
|
18312
19500
|
}
|
|
18313
19501
|
|
|
18314
19502
|
function nativeReturnedJsxValue(
|
|
@@ -18785,6 +19973,23 @@ function extractFragment(node, ctx, holeProps, parentNs = 'html') {
|
|
|
18785
19973
|
newAttrs.push(attr);
|
|
18786
19974
|
continue;
|
|
18787
19975
|
}
|
|
19976
|
+
if (attr._octaneBindingClassGroups) {
|
|
19977
|
+
const lift = (expression) => {
|
|
19978
|
+
const name = `h${holeProps.length}`;
|
|
19979
|
+
holeProps.push(objectProp(name, rewriteJsxValues(expression, ctx)));
|
|
19980
|
+
return memberProps(name, expression);
|
|
19981
|
+
};
|
|
19982
|
+
const group = attr._octaneBindingClassGroups;
|
|
19983
|
+
newAttrs.push({
|
|
19984
|
+
...attr,
|
|
19985
|
+
_octaneBindingClassGroups: {
|
|
19986
|
+
receipt: group.receipt,
|
|
19987
|
+
baseline: group.baseline.map(lift),
|
|
19988
|
+
groups: group.groups.map(lift),
|
|
19989
|
+
},
|
|
19990
|
+
});
|
|
19991
|
+
continue;
|
|
19992
|
+
}
|
|
18788
19993
|
const v = attr.value;
|
|
18789
19994
|
const inner = v && v.type === 'JSXExpressionContainer' ? v.expression : v;
|
|
18790
19995
|
// Dynamic attr = an expression value that isn't a static literal/string.
|
|
@@ -18853,9 +20058,10 @@ function extractFragment(node, ctx, holeProps, parentNs = 'html') {
|
|
|
18853
20058
|
const annotation = isKnownTextChildExpression(expr, ctx.knownStringChildLocals)
|
|
18854
20059
|
? b.ts_keyword_type('string')
|
|
18855
20060
|
: expr.typeAnnotation;
|
|
18856
|
-
newChildren.push(
|
|
18857
|
-
|
|
18858
|
-
|
|
20061
|
+
newChildren.push({
|
|
20062
|
+
...child,
|
|
20063
|
+
expression: b.ts_as(memberProps(hn, expr.expression), annotation),
|
|
20064
|
+
});
|
|
18859
20065
|
} else {
|
|
18860
20066
|
holeProps.push(objectProp(hn, rewriteExtractedFragmentHole(expr, ctx, childNs)));
|
|
18861
20067
|
// The renderer only sees `props.hN`, so it cannot rediscover an inline
|
|
@@ -18866,15 +20072,16 @@ function extractFragment(node, ctx, holeProps, parentNs = 'html') {
|
|
|
18866
20072
|
const rendered = isKnownTextChildExpression(expr, ctx.knownStringChildLocals)
|
|
18867
20073
|
? b.ts_as(member, b.ts_keyword_type('string'))
|
|
18868
20074
|
: member;
|
|
18869
|
-
newChildren.push(
|
|
20075
|
+
newChildren.push({ ...child, expression: rendered });
|
|
18870
20076
|
}
|
|
18871
20077
|
} else if (t === 'Element' || t === 'JSXElement') {
|
|
18872
20078
|
if (isLongFormTemplateSentinel(child, childNs, true, ctx)) {
|
|
18873
20079
|
newChildren.push(extractFragment(child, ctx, holeProps, childNs));
|
|
18874
20080
|
} else if (
|
|
18875
20081
|
isComponentTag(child) &&
|
|
18876
|
-
|
|
18877
|
-
|
|
20082
|
+
(child._octaneBindingSite ||
|
|
20083
|
+
(ctx._foldCtx?.templateComponentChildren === true &&
|
|
20084
|
+
requiresTemplateNormalization(child, childNs, true, ctx)))
|
|
18878
20085
|
) {
|
|
18879
20086
|
// Component children normally become descriptor children at return-value
|
|
18880
20087
|
// position. A directive cannot be represented by lowerJsxChild, though, so
|
|
@@ -18943,7 +20150,12 @@ function extractFragment(node, ctx, holeProps, parentNs = 'html') {
|
|
|
18943
20150
|
// before fragment extraction. It is still an owner-side renderable hole.
|
|
18944
20151
|
const hn = `h${holeProps.length}`;
|
|
18945
20152
|
holeProps.push(objectProp(hn, rewriteExtractedFragmentHole(child.expression, ctx, childNs)));
|
|
18946
|
-
newChildren.push(
|
|
20153
|
+
newChildren.push({
|
|
20154
|
+
...b.jsx_expression_container(memberProps(hn, child)),
|
|
20155
|
+
...(child._octaneBindingOpaque ? { _octaneBindingOpaque: child._octaneBindingOpaque } : {}),
|
|
20156
|
+
...(child._octaneBindingValue ? { _octaneBindingValue: child._octaneBindingValue } : {}),
|
|
20157
|
+
...(child._octaneBindingSlot ? { _octaneBindingSlot: child._octaneBindingSlot } : {}),
|
|
20158
|
+
});
|
|
18947
20159
|
} else if ((t === 'IfStatement' || t === 'JSXIfExpression') && ctx._foldCtx) {
|
|
18948
20160
|
// FOLD a directive: lower its branch bodies on the COMPONENT side (so the
|
|
18949
20161
|
// `__then$N`/`__else$N` helpers keep their closure over setup locals/props),
|
|
@@ -18960,7 +20172,12 @@ function extractFragment(node, ctx, holeProps, parentNs = 'html') {
|
|
|
18960
20172
|
inheritOriginLoc(b.if(child.test, child.consequent, child.alternate || null), child),
|
|
18961
20173
|
// The clause keyword's own span — `@else` starts before its
|
|
18962
20174
|
// block, so it is the only authored range that names the arm.
|
|
18963
|
-
{
|
|
20175
|
+
{
|
|
20176
|
+
alternateKeyword: child.alternateKeyword ?? null,
|
|
20177
|
+
...(child._octaneBindingSite
|
|
20178
|
+
? { _octaneBindingSite: child._octaneBindingSite }
|
|
20179
|
+
: {}),
|
|
20180
|
+
},
|
|
18964
20181
|
)
|
|
18965
20182
|
: child;
|
|
18966
20183
|
const ic = makeIfCall(ifNode, ctx, fc.compInlinedSubs, fc.parentNs, fc.cssHash);
|
|
@@ -19043,6 +20260,9 @@ function extractFragment(node, ctx, holeProps, parentNs = 'html') {
|
|
|
19043
20260
|
index: child.index || null,
|
|
19044
20261
|
empty: child.empty || null,
|
|
19045
20262
|
emptyKeyword: child.emptyKeyword ?? null,
|
|
20263
|
+
...(child._octaneBindingSite
|
|
20264
|
+
? { _octaneBindingSite: child._octaneBindingSite }
|
|
20265
|
+
: {}),
|
|
19046
20266
|
},
|
|
19047
20267
|
)
|
|
19048
20268
|
: child;
|
|
@@ -19350,9 +20570,17 @@ function lowerHostFragment(
|
|
|
19350
20570
|
} else {
|
|
19351
20571
|
ctx.hoistedHelpers.push(renderer);
|
|
19352
20572
|
}
|
|
19353
|
-
ctx.runtimeNeeded.add('
|
|
20573
|
+
ctx.runtimeNeeded.add('createElementFromConfig');
|
|
19354
20574
|
// The fragment-renderer call maps to the authored fragment root.
|
|
19355
|
-
return inheritOriginLoc(
|
|
20575
|
+
return inheritOriginLoc(
|
|
20576
|
+
b.call(
|
|
20577
|
+
'_$createElementFromConfig',
|
|
20578
|
+
b.literal(componentInvocationSite(ctx, node)),
|
|
20579
|
+
b.id(fragName),
|
|
20580
|
+
b.object(holeProps),
|
|
20581
|
+
),
|
|
20582
|
+
node,
|
|
20583
|
+
);
|
|
19356
20584
|
}
|
|
19357
20585
|
|
|
19358
20586
|
/**
|
|
@@ -19657,7 +20885,7 @@ function serverValueDirectiveFold(
|
|
|
19657
20885
|
directive,
|
|
19658
20886
|
),
|
|
19659
20887
|
);
|
|
19660
|
-
ctx.runtimeNeeded.add('
|
|
20888
|
+
ctx.runtimeNeeded.add('createElementFromConfig');
|
|
19661
20889
|
// The wrapper call stands where the directive was authored, and every printed
|
|
19662
20890
|
// node needs an origin (assertNodeLocs). Inside a component body the enclosing
|
|
19663
20891
|
// statement supplied one; a module-level statement does not.
|
|
@@ -19669,7 +20897,12 @@ function serverValueDirectiveFold(
|
|
|
19669
20897
|
);
|
|
19670
20898
|
}
|
|
19671
20899
|
return inheritOriginLoc(
|
|
19672
|
-
b.call(
|
|
20900
|
+
b.call(
|
|
20901
|
+
'_$createElementFromConfig',
|
|
20902
|
+
b.literal(componentInvocationSite(ctx, directive)),
|
|
20903
|
+
b.id(wrapperName),
|
|
20904
|
+
b.object(descriptorProps),
|
|
20905
|
+
),
|
|
19673
20906
|
directive,
|
|
19674
20907
|
);
|
|
19675
20908
|
};
|
|
@@ -20210,6 +21443,7 @@ function jsxElementToCreateElement(node, ctx, eagerRoot = false) {
|
|
|
20210
21443
|
const nameNode = node.openingElement?.name || node.id;
|
|
20211
21444
|
const activity = isActivityLongForm(node, ctx);
|
|
20212
21445
|
const componentTag = activity || isComponentTag(node);
|
|
21446
|
+
const invocationSite = componentTag ? componentInvocationSite(ctx, node) : null;
|
|
20213
21447
|
// Host (lowercase) tag → string literal (`'li'`) for the de-opt renderer;
|
|
20214
21448
|
// component (capitalized / member / dynamic) → the identifier/member ref.
|
|
20215
21449
|
const compNode = activity
|
|
@@ -20227,6 +21461,15 @@ function jsxElementToCreateElement(node, ctx, eagerRoot = false) {
|
|
|
20227
21461
|
const attrs = node.attributes || node.openingElement?.attributes || [];
|
|
20228
21462
|
const properties = [];
|
|
20229
21463
|
for (const attr of attrs) {
|
|
21464
|
+
if (
|
|
21465
|
+
!componentTag &&
|
|
21466
|
+
attr._octaneKnownAttributeSpread &&
|
|
21467
|
+
attr.type !== 'JSXSpreadAttribute' &&
|
|
21468
|
+
attr.type !== 'SpreadAttribute'
|
|
21469
|
+
)
|
|
21470
|
+
throw new Error(
|
|
21471
|
+
'Native JSX attribute shorthands are not supported in descriptor-backed JSX values; use an owned template element.',
|
|
21472
|
+
);
|
|
20230
21473
|
if (attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') {
|
|
20231
21474
|
properties.push(b.spread(rewriteJsxValues(attr.argument, ctx)));
|
|
20232
21475
|
continue;
|
|
@@ -20424,13 +21667,30 @@ function jsxElementToCreateElement(node, ctx, eagerRoot = false) {
|
|
|
20424
21667
|
? requireRuntimeForContext(ctx, 'nativeCreateScopedElement')
|
|
20425
21668
|
: rtAlias('createScopedElement');
|
|
20426
21669
|
const readChildren = inheritOriginLoc(b.arrow([], memoizedChildrenBody ?? childrenValue), node);
|
|
20427
|
-
descriptor = inheritOriginLoc(
|
|
21670
|
+
descriptor = inheritOriginLoc(
|
|
21671
|
+
b.call(
|
|
21672
|
+
scopedElement,
|
|
21673
|
+
compNode,
|
|
21674
|
+
propsNode,
|
|
21675
|
+
readChildren,
|
|
21676
|
+
...(invocationSite === null ? [] : [b.literal(invocationSite)]),
|
|
21677
|
+
),
|
|
21678
|
+
node,
|
|
21679
|
+
);
|
|
20428
21680
|
} else {
|
|
20429
|
-
ctx.runtimeNeeded.add('createElement');
|
|
21681
|
+
ctx.runtimeNeeded.add(componentTag ? 'createElementFromConfig' : 'createElement');
|
|
20430
21682
|
// Remaining scaffolding (callee, props object, spread/diagnostic wrappers,
|
|
20431
21683
|
// static-content literals) maps to the authored JSX element.
|
|
20432
21684
|
descriptor = inheritOriginLoc(
|
|
20433
|
-
|
|
21685
|
+
componentTag
|
|
21686
|
+
? b.call(
|
|
21687
|
+
'_$createElementFromConfig',
|
|
21688
|
+
b.literal(invocationSite),
|
|
21689
|
+
compNode,
|
|
21690
|
+
propsNode,
|
|
21691
|
+
...(loweredChildren.length ? [b.array(loweredChildren)] : []),
|
|
21692
|
+
)
|
|
21693
|
+
: b.call('_$createElement', compNode, propsNode, ...loweredChildren),
|
|
20434
21694
|
node,
|
|
20435
21695
|
);
|
|
20436
21696
|
}
|
|
@@ -21074,6 +22334,14 @@ function rewriteOpaqueTitles(node, ctx, namespace = 'html') {
|
|
|
21074
22334
|
function headAttrsExpression(el) {
|
|
21075
22335
|
const attrProps = [];
|
|
21076
22336
|
for (const a of el.openingElement.attributes || []) {
|
|
22337
|
+
if (
|
|
22338
|
+
a._octaneKnownAttributeSpread &&
|
|
22339
|
+
a.type !== 'JSXSpreadAttribute' &&
|
|
22340
|
+
a.type !== 'SpreadAttribute'
|
|
22341
|
+
)
|
|
22342
|
+
throw new Error(
|
|
22343
|
+
'Native JSX attribute shorthands are not supported on hoisted head elements.',
|
|
22344
|
+
);
|
|
21077
22345
|
if (a.type === 'SpreadAttribute' || a.type === 'JSXSpreadAttribute') {
|
|
21078
22346
|
attrProps.push(b.spread(a.argument));
|
|
21079
22347
|
continue;
|
|
@@ -21457,6 +22725,14 @@ function normalizeChildren(
|
|
|
21457
22725
|
out.push({
|
|
21458
22726
|
type: needsRichDispatch(expression) ? 'TSRXExpression' : 'Text',
|
|
21459
22727
|
expression,
|
|
22728
|
+
...(n._octaneBindingText === undefined ? {} : { _octaneBindingText: n._octaneBindingText }),
|
|
22729
|
+
...(n._octaneBindingOpaque === undefined
|
|
22730
|
+
? {}
|
|
22731
|
+
: { _octaneBindingOpaque: n._octaneBindingOpaque }),
|
|
22732
|
+
...(n._octaneBindingValue === undefined
|
|
22733
|
+
? {}
|
|
22734
|
+
: { _octaneBindingValue: n._octaneBindingValue }),
|
|
22735
|
+
...(n._octaneBindingSlot === undefined ? {} : { _octaneBindingSlot: n._octaneBindingSlot }),
|
|
21460
22736
|
});
|
|
21461
22737
|
} else if (n.type === 'JSXElement') {
|
|
21462
22738
|
// Long-form `<Fragment>…</Fragment>` (canary `enableFragmentRefs`
|
|
@@ -21573,6 +22849,7 @@ function normalizeChildren(
|
|
|
21573
22849
|
selfClosing: n.openingElement.selfClosing,
|
|
21574
22850
|
loc: n.loc, // preserve element position for dev hydration LOC (component slots)
|
|
21575
22851
|
};
|
|
22852
|
+
if (n._octaneBindingSite !== undefined) element._octaneBindingSite = n._octaneBindingSite;
|
|
21576
22853
|
// `unstable_Activity` starts with a lowercase letter but is a component
|
|
21577
22854
|
// when it resolves to the builtin. Keep that fact on this rare node only.
|
|
21578
22855
|
if (isActivityLongForm(n, ctx)) element.activityDescriptor = true;
|
|
@@ -21624,6 +22901,9 @@ function normalizeChildren(
|
|
|
21624
22901
|
out.push(
|
|
21625
22902
|
Object.assign(inheritOriginLoc(b.if(n.test, n.consequent, n.alternate || null), n), {
|
|
21626
22903
|
alternateKeyword: n.alternateKeyword ?? null,
|
|
22904
|
+
...(n._octaneBindingSite === undefined
|
|
22905
|
+
? {}
|
|
22906
|
+
: { _octaneBindingSite: n._octaneBindingSite }),
|
|
21627
22907
|
}),
|
|
21628
22908
|
);
|
|
21629
22909
|
} else if (n.type === 'JSXForExpression') {
|
|
@@ -21637,6 +22917,9 @@ function normalizeChildren(
|
|
|
21637
22917
|
index: n.index || null,
|
|
21638
22918
|
empty: n.empty || null,
|
|
21639
22919
|
emptyKeyword: n.emptyKeyword ?? null,
|
|
22920
|
+
...(n._octaneBindingSite === undefined
|
|
22921
|
+
? {}
|
|
22922
|
+
: { _octaneBindingSite: n._octaneBindingSite }),
|
|
21640
22923
|
}),
|
|
21641
22924
|
);
|
|
21642
22925
|
} else if (n.type === 'JSXTryExpression') {
|
|
@@ -22358,8 +23641,24 @@ function stripTsOnlyWrappers(node) {
|
|
|
22358
23641
|
function nativeReadActivationNodes(ctx, origin) {
|
|
22359
23642
|
// Install the graph-free driver before any authored root invocation, not
|
|
22360
23643
|
// after parameter evaluation inside a syntactically recognized component.
|
|
22361
|
-
//
|
|
22362
|
-
|
|
23644
|
+
// Binding-only activation modules use their selected native adapters, not
|
|
23645
|
+
// this renderer driver. Retain the plain-module renderer-import policy for
|
|
23646
|
+
// hand-authored createElement/root calls without generated runtime helpers.
|
|
23647
|
+
if (
|
|
23648
|
+
!ctx.nativeReads ||
|
|
23649
|
+
(ctx.runtimeNeeded.size === 0 &&
|
|
23650
|
+
!ctx.activityModuleAst.body.some(
|
|
23651
|
+
(node) =>
|
|
23652
|
+
node.type === 'ImportDeclaration' &&
|
|
23653
|
+
node.importKind !== 'type' &&
|
|
23654
|
+
['octane', 'octane/server', 'octane/signals/client', 'octane/signals/server'].includes(
|
|
23655
|
+
node.source?.value,
|
|
23656
|
+
) &&
|
|
23657
|
+
(node.specifiers.length === 0 ||
|
|
23658
|
+
node.specifiers.some((specifier) => specifier.importKind !== 'type')),
|
|
23659
|
+
))
|
|
23660
|
+
)
|
|
23661
|
+
return [];
|
|
22363
23662
|
return [
|
|
22364
23663
|
inheritOriginLoc(
|
|
22365
23664
|
b.stmt(b.call(requireRuntimeForContext(ctx, 'enableNativeReadCollection'), b.literal(1))),
|
|
@@ -22954,7 +24253,7 @@ function planJsx(
|
|
|
22954
24253
|
// A dynamic text hole with a text-producing neighbour: the server emits a
|
|
22955
24254
|
// `<!-- -->` separator between the two texts (else the parser would merge
|
|
22956
24255
|
// them), and only the hole-aware sibling() walk knows to step across it.
|
|
22957
|
-
elementBindings.some((b) => b.kind === 'text' && b.adjacentText);
|
|
24256
|
+
elementBindings.some((b) => b.kind === 'text' && (b.adjacentText || b.bindingMarker));
|
|
22958
24257
|
if (hasHoles) {
|
|
22959
24258
|
ctx.runtimeNeeded.add('child');
|
|
22960
24259
|
ctx.runtimeNeeded.add('sibling');
|
|
@@ -23144,14 +24443,21 @@ function planJsx(
|
|
|
23144
24443
|
// htext/htextSwap/setAttribute (which only have the element) can report `file:line:col`.
|
|
23145
24444
|
if (_locStamped && b.kind !== 'text') stampHostLoc(elVar, b.path, undefined);
|
|
23146
24445
|
else if (_locStamped) stampHostLoc(ensureVar(b.path), b.path, undefined);
|
|
23147
|
-
|
|
23148
|
-
if (
|
|
24446
|
+
const signalHelper = directSignalBindingHelper(b);
|
|
24447
|
+
if (signalHelper !== null) {
|
|
24448
|
+
ctx.runtimeNeeded.add(signalHelper);
|
|
24449
|
+
registerAttrLoweringOrigin(ctx, b.nameOrigin, signalHelper, b.name);
|
|
24450
|
+
}
|
|
24451
|
+
if (!b.signalDirect && (b.kind === 'text' || b.kind === 'textOnlyChild')) {
|
|
24452
|
+
ctx.runtimeNeeded.add('setText');
|
|
24453
|
+
}
|
|
24454
|
+
if (b.kind === 'text') ctx.runtimeNeeded.add(b.bindingMarker ? 'bindingText' : 'htextSwap');
|
|
23149
24455
|
if (b.kind === 'textOnlyChild') ctx.runtimeNeeded.add('htext');
|
|
23150
24456
|
// Mounts use the unconditional writer; reactive scalar bindings use either
|
|
23151
24457
|
// that writer behind an inline guard or its compact comparison helper.
|
|
23152
24458
|
// Claim both authored-name tokens when both paths exist, and do not retain
|
|
23153
24459
|
// a mount-only import for deferred writes.
|
|
23154
|
-
const attrHelper = attrBindingHelper(b);
|
|
24460
|
+
const attrHelper = b.signalDirect ? null : attrBindingHelper(b);
|
|
23155
24461
|
if (attrHelper !== null) {
|
|
23156
24462
|
if (b.kind === 'class' && b.fresh && (!b.deferred || (!b.mountOnly && inlineBindingGuards)))
|
|
23157
24463
|
ctx.runtimeNeeded.add('normalizeClass');
|
|
@@ -23169,9 +24475,12 @@ function planJsx(
|
|
|
23169
24475
|
if (b.kind === 'dangerCommit') ctx.runtimeNeeded.add('setDangerouslySetInnerHTMLSources');
|
|
23170
24476
|
if (b.kind === 'formCommit') ctx.runtimeNeeded.add('setFormControlSources');
|
|
23171
24477
|
if (b.kind === 'hostCommit') {
|
|
23172
|
-
ctx.runtimeNeeded.add('setHostPropSources');
|
|
23173
24478
|
if (ctx.nativeReads) b.readStyle = requireRuntimeForContext(ctx, 'readNativeDomStyle');
|
|
23174
|
-
ctx.runtimeNeeded.add('
|
|
24479
|
+
if (b.signalHostSources) ctx.runtimeNeeded.add('bindSignalHostPropSources');
|
|
24480
|
+
else {
|
|
24481
|
+
ctx.runtimeNeeded.add('setHostPropSources');
|
|
24482
|
+
ctx.runtimeNeeded.add('queueOwnRefDetach');
|
|
24483
|
+
}
|
|
23175
24484
|
}
|
|
23176
24485
|
// A commit-phase collector takes the element's props as one grouped call,
|
|
23177
24486
|
// so each source's name literal — not the shared helper — is what claims
|
|
@@ -23206,9 +24515,12 @@ function planJsx(
|
|
|
23206
24515
|
ctx.runtimeNeeded.add(b.spread ? 'canSplitStyleProperties' : 'isHydratingStyle');
|
|
23207
24516
|
if (b.spread) ctx.runtimeNeeded.add('styleObjectPrototype');
|
|
23208
24517
|
}
|
|
23209
|
-
if (b.kind === 'nativeStyle') {
|
|
24518
|
+
if (b.kind === 'nativeStyle' || b.kind === 'nativeProjection') {
|
|
23210
24519
|
b.slotIndex = ++nativeStyleSlots;
|
|
23211
|
-
b.helper = requireRuntimeForContext(
|
|
24520
|
+
b.helper = requireRuntimeForContext(
|
|
24521
|
+
ctx,
|
|
24522
|
+
b.kind === 'nativeStyle' ? 'nativeStyleBinding' : 'nativeProjectionBinding',
|
|
24523
|
+
);
|
|
23212
24524
|
}
|
|
23213
24525
|
if (b.kind === 'spread') {
|
|
23214
24526
|
ctx.runtimeNeeded.add('setSpread');
|
|
@@ -23831,18 +25143,26 @@ function planJsx(
|
|
|
23831
25143
|
ctx.runtimeNeeded.add('ifBlock');
|
|
23832
25144
|
registerDirectiveOrigin(ctx, org, ['_$ifBlock', ic.thenHelper, ic.elseHelper]);
|
|
23833
25145
|
registerClauseOrigin(ctx, ic.alternateKeyword, [ic.elseHelper]);
|
|
23834
|
-
|
|
23835
|
-
|
|
23836
|
-
|
|
23837
|
-
|
|
23838
|
-
|
|
23839
|
-
|
|
23840
|
-
|
|
23841
|
-
|
|
23842
|
-
|
|
23843
|
-
...trailing,
|
|
23844
|
-
),
|
|
25146
|
+
let invocation = b.call(
|
|
25147
|
+
'_$ifBlock',
|
|
25148
|
+
b.id('__s'),
|
|
25149
|
+
b.literal(slotIndex),
|
|
25150
|
+
hostExpr,
|
|
25151
|
+
condition,
|
|
25152
|
+
helperRefNode(ic.thenHelper),
|
|
25153
|
+
inheritOriginLoc(helperRefNode(ic.elseHelper ?? 'null'), ic.alternateKeyword),
|
|
25154
|
+
...trailing,
|
|
23845
25155
|
);
|
|
25156
|
+
if (ctx.presentationHydration?.structural && ic.bindingSite) {
|
|
25157
|
+
invocation = b.call(
|
|
25158
|
+
requireRuntimeForContext(ctx, 'presentationStructure'),
|
|
25159
|
+
invocation.callee,
|
|
25160
|
+
b.literal('if'),
|
|
25161
|
+
b.literal(bindingMarker(ic.bindingSite, '')),
|
|
25162
|
+
...invocation.arguments,
|
|
25163
|
+
);
|
|
25164
|
+
}
|
|
25165
|
+
const call = b.stmt(invocation);
|
|
23846
25166
|
pushAfterStmt(ic.id, org, conditionDeclaration ? b.block([conditionDeclaration, call]) : call);
|
|
23847
25167
|
}
|
|
23848
25168
|
for (const cc of compCalls) {
|
|
@@ -23850,7 +25170,10 @@ function planJsx(
|
|
|
23850
25170
|
const org = cc.origin ?? planOrigin;
|
|
23851
25171
|
const hostExpr = () => hostNodeFor(`_compHost$${cc.id}`);
|
|
23852
25172
|
if (cc.hostChildrenBinding != null) {
|
|
23853
|
-
const props = () =>
|
|
25173
|
+
const props = () => {
|
|
25174
|
+
const host = bagFieldNode(bag, `_host$${cc.hostChildrenBinding.id}`);
|
|
25175
|
+
return cc.hostChildrenBinding.signalHostSources ? b.member(host, 'resolved') : host;
|
|
25176
|
+
};
|
|
23854
25177
|
cc.valueExpr = b.conditional(
|
|
23855
25178
|
propertyIsEnumerableCall(props(), 'children'),
|
|
23856
25179
|
b.member(props(), 'children'),
|
|
@@ -23865,34 +25188,87 @@ function planJsx(
|
|
|
23865
25188
|
// component calls; only the emitted runtime call differs.
|
|
23866
25189
|
if (cc.isChild) {
|
|
23867
25190
|
const V = () => b.id('_v');
|
|
23868
|
-
|
|
23869
|
-
|
|
23870
|
-
|
|
23871
|
-
|
|
23872
|
-
|
|
23873
|
-
|
|
23874
|
-
|
|
23875
|
-
|
|
23876
|
-
|
|
23877
|
-
|
|
23878
|
-
|
|
23879
|
-
|
|
23880
|
-
|
|
23881
|
-
|
|
23882
|
-
|
|
23883
|
-
|
|
23884
|
-
|
|
23885
|
-
|
|
23886
|
-
|
|
23887
|
-
|
|
23888
|
-
|
|
23889
|
-
|
|
23890
|
-
|
|
23891
|
-
|
|
23892
|
-
|
|
23893
|
-
|
|
23894
|
-
|
|
23895
|
-
|
|
25191
|
+
const childAnchor = anchorNodeFor(cc, 'compAnchor');
|
|
25192
|
+
if (cc.bindingMarker) {
|
|
25193
|
+
ctx.runtimeNeeded.add('bindingChildSlot');
|
|
25194
|
+
pushAfterStmt(
|
|
25195
|
+
cc.id,
|
|
25196
|
+
org,
|
|
25197
|
+
b.stmt(
|
|
25198
|
+
b.call(
|
|
25199
|
+
'_$bindingChildSlot',
|
|
25200
|
+
b.id('__s'),
|
|
25201
|
+
b.literal(slotIndex),
|
|
25202
|
+
hostExpr(),
|
|
25203
|
+
cc.valueExpr,
|
|
25204
|
+
b.literal(cc.bindingMarker),
|
|
25205
|
+
childAnchor ?? b.literal(null),
|
|
25206
|
+
cc.anchorVar ? b.literal(true) : undefinedNode(),
|
|
25207
|
+
),
|
|
25208
|
+
),
|
|
25209
|
+
);
|
|
25210
|
+
continue;
|
|
25211
|
+
}
|
|
25212
|
+
if (!noTemplate && cc.signalSite != null && (ctx.signalBindingsUsed || ctx.nativeReads)) {
|
|
25213
|
+
ctx.signalBindingsUsed = true;
|
|
25214
|
+
ctx.signalBindingsEager = true;
|
|
25215
|
+
ctx.runtimeNeeded.add('bindSignalChild');
|
|
25216
|
+
const tokenKey = `_sigch$${cc.id}`;
|
|
25217
|
+
bag.constField(tokenKey, b.literal(null));
|
|
25218
|
+
pushAfterStmt(
|
|
25219
|
+
cc.id,
|
|
25220
|
+
org,
|
|
25221
|
+
b.stmt(
|
|
25222
|
+
b.assignment(
|
|
25223
|
+
'=',
|
|
25224
|
+
bagFieldNode(bag, tokenKey),
|
|
25225
|
+
b.call(
|
|
25226
|
+
'_$bindSignalChild',
|
|
25227
|
+
b.id('__s'),
|
|
25228
|
+
bagFieldNode(bag, tokenKey),
|
|
25229
|
+
b.literal(slotIndex),
|
|
25230
|
+
hostExpr(),
|
|
25231
|
+
cc.valueExpr,
|
|
25232
|
+
b.literal(cc.signalSite),
|
|
25233
|
+
childAnchor ?? b.literal(null),
|
|
25234
|
+
cc.anchorVar ? b.literal(true) : undefinedNode(),
|
|
25235
|
+
undefinedNode(),
|
|
25236
|
+
cc.coalesceRange ? b.literal(true) : undefinedNode(),
|
|
25237
|
+
cc.onlyChildText ? b.literal(true) : undefinedNode(),
|
|
25238
|
+
),
|
|
25239
|
+
),
|
|
25240
|
+
),
|
|
25241
|
+
);
|
|
25242
|
+
continue;
|
|
25243
|
+
}
|
|
25244
|
+
// MARKERLESS only-child renderable: append a primitive as a single Text
|
|
25245
|
+
// node (no `<!>`, no slot state). Either the shared helper or the inline
|
|
25246
|
+
// row guard compares its cached value/node before the ordinary child path.
|
|
25247
|
+
if (cc.onlyChildText && !noTemplate) {
|
|
25248
|
+
const chp = () => bagFieldNode(bag, `_chp$${cc.id}`);
|
|
25249
|
+
const chv = () => bagFieldNode(bag, `_chv$${cc.id}`);
|
|
25250
|
+
// A host that can receive dangerouslySetInnerHTML must validate its
|
|
25251
|
+
// current child on EVERY render. The ordinary primitive fast path can
|
|
25252
|
+
// identity-skip an unchanged child while a raw-HTML writer activates in
|
|
25253
|
+
// the same render, bypassing childTextHole's mutual-exclusion check.
|
|
25254
|
+
if (cc.potentialDangerouslySetInnerHTML) {
|
|
25255
|
+
ctx.runtimeNeeded.add('childTextHole');
|
|
25256
|
+
pushAfterStmt(
|
|
25257
|
+
cc.id,
|
|
25258
|
+
org,
|
|
25259
|
+
b.block([
|
|
25260
|
+
b.const('_v', cc.valueExpr),
|
|
25261
|
+
b.stmt(
|
|
25262
|
+
b.assignment(
|
|
25263
|
+
'=',
|
|
25264
|
+
chv(),
|
|
25265
|
+
b.call(
|
|
25266
|
+
'_$childTextHole',
|
|
25267
|
+
b.id('__s'),
|
|
25268
|
+
b.literal(slotIndex),
|
|
25269
|
+
hostExpr(),
|
|
25270
|
+
V(),
|
|
25271
|
+
chv(),
|
|
23896
25272
|
),
|
|
23897
25273
|
),
|
|
23898
25274
|
),
|
|
@@ -23971,7 +25347,6 @@ function planJsx(
|
|
|
23971
25347
|
}
|
|
23972
25348
|
// Anchor node (null = append into an in-template element host) — see
|
|
23973
25349
|
// anchorNodeFor.
|
|
23974
|
-
const childAnchor = anchorNodeFor(cc, 'compAnchor');
|
|
23975
25350
|
if (noTemplate) {
|
|
23976
25351
|
// No bag to cache on → the small `textSlot` wrapper (fast inline for a
|
|
23977
25352
|
// primitive into a text slot; delegates to `childSlot` otherwise).
|
|
@@ -24056,17 +25431,36 @@ function planJsx(
|
|
|
24056
25431
|
liteMemo ? 'componentSlotLite' : cc.voidComponent ? 'componentSlotVoid' : 'componentSlot',
|
|
24057
25432
|
);
|
|
24058
25433
|
const memoAnchor = anchorNodeFor(cc, 'compAnchor');
|
|
24059
|
-
const trailing =
|
|
24060
|
-
|
|
24061
|
-
|
|
24062
|
-
|
|
24063
|
-
|
|
24064
|
-
|
|
24065
|
-
|
|
24066
|
-
|
|
25434
|
+
const trailing = liteMemo
|
|
25435
|
+
? [memoAnchor ?? undefinedNode(), b.literal(cc.invocationSite)]
|
|
25436
|
+
: [
|
|
25437
|
+
memoAnchor ?? undefinedNode(),
|
|
25438
|
+
undefinedNode(),
|
|
25439
|
+
cc.singleRoot ? b.literal(true) : undefinedNode(),
|
|
25440
|
+
cc.inheritRange ? b.literal(true) : undefinedNode(),
|
|
25441
|
+
undefinedNode(),
|
|
25442
|
+
b.literal(cc.invocationSite),
|
|
25443
|
+
];
|
|
24067
25444
|
const witnessMiss = cc.autoMemoWitnesses.length
|
|
24068
25445
|
? witnessMissChain(cc.autoMemoWitnesses)
|
|
24069
25446
|
: null;
|
|
25447
|
+
let invocation = b.call(
|
|
25448
|
+
componentHelper,
|
|
25449
|
+
b.id('__s'),
|
|
25450
|
+
b.literal(slotIndex),
|
|
25451
|
+
hostExpr(),
|
|
25452
|
+
cc.compNode,
|
|
25453
|
+
cc.propsExpr,
|
|
25454
|
+
...trailing,
|
|
25455
|
+
);
|
|
25456
|
+
if (ctx.presentationHydration?.structural && cc.bindingSite)
|
|
25457
|
+
invocation = b.call(
|
|
25458
|
+
requireRuntimeForContext(ctx, 'presentationStructure'),
|
|
25459
|
+
invocation.callee,
|
|
25460
|
+
b.literal('view'),
|
|
25461
|
+
b.literal(bindingMarker(cc.bindingSite, 'v')),
|
|
25462
|
+
...invocation.arguments,
|
|
25463
|
+
);
|
|
24070
25464
|
pushAfterStmt(
|
|
24071
25465
|
cc.id,
|
|
24072
25466
|
org,
|
|
@@ -24074,17 +25468,7 @@ function planJsx(
|
|
|
24074
25468
|
ctx,
|
|
24075
25469
|
cc.autoMemoDeps,
|
|
24076
25470
|
slotIndex,
|
|
24077
|
-
b.stmt(
|
|
24078
|
-
b.call(
|
|
24079
|
-
componentHelper,
|
|
24080
|
-
b.id('__s'),
|
|
24081
|
-
b.literal(slotIndex),
|
|
24082
|
-
hostExpr(),
|
|
24083
|
-
cc.compNode,
|
|
24084
|
-
cc.propsExpr,
|
|
24085
|
-
...trailing,
|
|
24086
|
-
),
|
|
24087
|
-
),
|
|
25471
|
+
b.stmt(invocation),
|
|
24088
25472
|
witnessMiss,
|
|
24089
25473
|
cc.autoMemoContextAware,
|
|
24090
25474
|
depNodeFor(cc),
|
|
@@ -24124,6 +25508,8 @@ function planJsx(
|
|
|
24124
25508
|
undefinedNode(),
|
|
24125
25509
|
undefinedNode(),
|
|
24126
25510
|
b.literal(true),
|
|
25511
|
+
undefinedNode(),
|
|
25512
|
+
b.literal(cc.invocationSite),
|
|
24127
25513
|
),
|
|
24128
25514
|
),
|
|
24129
25515
|
);
|
|
@@ -24150,7 +25536,8 @@ function planJsx(
|
|
|
24150
25536
|
hostExpr(),
|
|
24151
25537
|
cc.compNode,
|
|
24152
25538
|
cc.propsExpr,
|
|
24153
|
-
|
|
25539
|
+
liteAnchor ?? undefinedNode(),
|
|
25540
|
+
b.literal(cc.invocationSite),
|
|
24154
25541
|
),
|
|
24155
25542
|
),
|
|
24156
25543
|
);
|
|
@@ -24163,52 +25550,32 @@ function planJsx(
|
|
|
24163
25550
|
// unmount move the slot DOM along with the block; an element host with
|
|
24164
25551
|
// no in-template anchor can safely append).
|
|
24165
25552
|
const compAnchor = anchorNodeFor(cc, 'compAnchor');
|
|
24166
|
-
const trailing = [
|
|
24167
|
-
|
|
24168
|
-
|
|
24169
|
-
|
|
24170
|
-
|
|
24171
|
-
|
|
24172
|
-
|
|
24173
|
-
|
|
24174
|
-
|
|
24175
|
-
|
|
24176
|
-
|
|
24177
|
-
|
|
24178
|
-
|
|
24179
|
-
|
|
24180
|
-
|
|
24181
|
-
trailing
|
|
24182
|
-
}
|
|
24183
|
-
// singleRoot is the 8th positional arg (after anchor, key). It's gated on
|
|
24184
|
-
// no-key, so backfill anchor + key placeholders to land it in the right
|
|
24185
|
-
// slot. `true` = proven same-module single-element root; `2` = the
|
|
24186
|
-
// cross-module sentinel (runtime checks the callee's $$singleRoot stamp).
|
|
24187
|
-
if (cc.singleRoot || cc.maybeSingleRoot) {
|
|
24188
|
-
if (!anchorFilled) {
|
|
24189
|
-
trailing.push(undefinedNode());
|
|
24190
|
-
anchorFilled = true;
|
|
24191
|
-
}
|
|
24192
|
-
trailing.push(undefinedNode(), cc.singleRoot ? b.literal(true) : b.literal(2));
|
|
24193
|
-
}
|
|
24194
|
-
// Persist key ownership separately from the current key VALUE so
|
|
24195
|
-
// `key={undefined}` remains an independent reconciliation boundary.
|
|
24196
|
-
if (cc.keyExpr != null) trailing.push(undefinedNode(), undefinedNode(), b.literal(true));
|
|
24197
|
-
pushAfterStmt(
|
|
24198
|
-
cc.id,
|
|
24199
|
-
org,
|
|
24200
|
-
b.stmt(
|
|
24201
|
-
b.call(
|
|
24202
|
-
componentHelper,
|
|
24203
|
-
b.id('__s'),
|
|
24204
|
-
b.literal(slotIndex),
|
|
24205
|
-
hostExpr(),
|
|
24206
|
-
cc.compNode,
|
|
24207
|
-
cc.propsExpr,
|
|
24208
|
-
...trailing,
|
|
24209
|
-
),
|
|
24210
|
-
),
|
|
25553
|
+
const trailing = [
|
|
25554
|
+
compAnchor ?? undefinedNode(),
|
|
25555
|
+
cc.keyExpr ?? undefinedNode(),
|
|
25556
|
+
cc.singleRoot ? b.literal(true) : cc.maybeSingleRoot ? b.literal(2) : undefinedNode(),
|
|
25557
|
+
undefinedNode(),
|
|
25558
|
+
cc.keyExpr != null ? b.literal(true) : undefinedNode(),
|
|
25559
|
+
b.literal(cc.invocationSite),
|
|
25560
|
+
];
|
|
25561
|
+
let invocation = b.call(
|
|
25562
|
+
componentHelper,
|
|
25563
|
+
b.id('__s'),
|
|
25564
|
+
b.literal(slotIndex),
|
|
25565
|
+
hostExpr(),
|
|
25566
|
+
cc.compNode,
|
|
25567
|
+
cc.propsExpr,
|
|
25568
|
+
...trailing,
|
|
24211
25569
|
);
|
|
25570
|
+
if (ctx.presentationHydration?.structural && cc.bindingSite)
|
|
25571
|
+
invocation = b.call(
|
|
25572
|
+
requireRuntimeForContext(ctx, 'presentationStructure'),
|
|
25573
|
+
invocation.callee,
|
|
25574
|
+
b.literal('view'),
|
|
25575
|
+
b.literal(bindingMarker(cc.bindingSite, 'v')),
|
|
25576
|
+
...invocation.arguments,
|
|
25577
|
+
);
|
|
25578
|
+
pushAfterStmt(cc.id, org, b.stmt(invocation));
|
|
24212
25579
|
}
|
|
24213
25580
|
for (const pc of ctx._portalCalls) {
|
|
24214
25581
|
const slotIndex = pc.slotIndex;
|
|
@@ -24585,6 +25952,16 @@ function commitSourceRows(sources, valueOf) {
|
|
|
24585
25952
|
return b.array(rows);
|
|
24586
25953
|
}
|
|
24587
25954
|
|
|
25955
|
+
// Preserve the authored rest site's identity through known-spread expansion.
|
|
25956
|
+
// Its authored spread ordinal is not the eventual host-source row index.
|
|
25957
|
+
function presentationHostSourcesCall(bind, ...args) {
|
|
25958
|
+
const call = b.call('_$bindSignalHostPropSources', ...args);
|
|
25959
|
+
const receipts = bind.sources.flatMap(({ binding }, source) =>
|
|
25960
|
+
binding.bindingRest ? [{ ...binding.bindingRest, source }] : [],
|
|
25961
|
+
);
|
|
25962
|
+
return receipts.length === 0 ? call : { ...call, _octanePresentationHostSources: receipts };
|
|
25963
|
+
}
|
|
25964
|
+
|
|
24588
25965
|
// Recombine a native spread snapshot only for mount/hydration or a key collision.
|
|
24589
25966
|
// Spreading the snapshot cannot replay getters: the authored spreads already
|
|
24590
25967
|
// copied their values before any of the trailing expressions were evaluated.
|
|
@@ -24649,9 +26026,11 @@ function emitDeferredMount(bind, elVar, bag) {
|
|
|
24649
26026
|
// Whole-object and spread-prefix styles diff on `_sty`; other grouped styles
|
|
24650
26027
|
// use it only as a first-render marker. Each trailing property keeps a scalar.
|
|
24651
26028
|
bag.constField(
|
|
24652
|
-
bind.
|
|
24653
|
-
? `
|
|
24654
|
-
:
|
|
26029
|
+
bind.signalDirect
|
|
26030
|
+
? `_sig$${bind.id}`
|
|
26031
|
+
: bind.kind === 'style' || bind.kind === 'styleProperties'
|
|
26032
|
+
? `_sty$${bind.id}`
|
|
26033
|
+
: `_prev$${bind.id}`,
|
|
24655
26034
|
bind.kind === 'styleProperty' || bind.kind === 'styleProperties' ? 'style-unset' : 'undefined',
|
|
24656
26035
|
);
|
|
24657
26036
|
if (bind.kind === 'styleProperties') {
|
|
@@ -24675,7 +26054,57 @@ function bindingOrigin(bind) {
|
|
|
24675
26054
|
return bind.expr && bind.expr.loc ? bind.expr : bind.origin;
|
|
24676
26055
|
}
|
|
24677
26056
|
|
|
26057
|
+
function directSignalBindingHelper(bind) {
|
|
26058
|
+
if (!bind.signalDirect) return null;
|
|
26059
|
+
if (bind.kind === 'text' || bind.kind === 'textOnlyChild') return 'bindSignalText';
|
|
26060
|
+
if (bind.kind === 'value' || bind.kind === 'selectValue') return 'bindSignalValue';
|
|
26061
|
+
if (bind.kind === 'checked' || bind.kind === 'checkedCheckable') return 'bindSignalChecked';
|
|
26062
|
+
return 'bindSignalAttribute';
|
|
26063
|
+
}
|
|
26064
|
+
|
|
26065
|
+
function directSignalBindingArgs(bind, host, previous, value = bind.expr, previousValue) {
|
|
26066
|
+
const common = [b.id('__s'), previous, host];
|
|
26067
|
+
if (bind.kind === 'text' || bind.kind === 'textOnlyChild') {
|
|
26068
|
+
return [
|
|
26069
|
+
...common,
|
|
26070
|
+
value,
|
|
26071
|
+
b.literal(bind.signalSite, JSON.stringify(bind.signalSite)),
|
|
26072
|
+
b.literal(bind.kind === 'textOnlyChild'),
|
|
26073
|
+
...(previousValue !== undefined
|
|
26074
|
+
? [
|
|
26075
|
+
bind.seededText ? b.literal(1) : undefinedNode(),
|
|
26076
|
+
bind.bindingMarker ? b.literal(bind.bindingMarker) : undefinedNode(),
|
|
26077
|
+
previousValue,
|
|
26078
|
+
]
|
|
26079
|
+
: bind.bindingMarker
|
|
26080
|
+
? [bind.seededText ? b.literal(1) : undefinedNode(), b.literal(bind.bindingMarker)]
|
|
26081
|
+
: bind.seededText
|
|
26082
|
+
? [b.literal(1)]
|
|
26083
|
+
: []),
|
|
26084
|
+
];
|
|
26085
|
+
}
|
|
26086
|
+
if (
|
|
26087
|
+
bind.kind === 'value' ||
|
|
26088
|
+
bind.kind === 'selectValue' ||
|
|
26089
|
+
bind.kind === 'checked' ||
|
|
26090
|
+
bind.kind === 'checkedCheckable'
|
|
26091
|
+
) {
|
|
26092
|
+
return [...common, bind.expr, b.literal(bind.signalSite, JSON.stringify(bind.signalSite))];
|
|
26093
|
+
}
|
|
26094
|
+
return [
|
|
26095
|
+
...common,
|
|
26096
|
+
attrLoweringToken(b.literal(bind.name), bind),
|
|
26097
|
+
bind.expr,
|
|
26098
|
+
b.literal(bind.signalSite, JSON.stringify(bind.signalSite)),
|
|
26099
|
+
b.literal(bind.kind, JSON.stringify(bind.kind)),
|
|
26100
|
+
];
|
|
26101
|
+
}
|
|
26102
|
+
|
|
24678
26103
|
function emitBindingMount(bind, elVar, bag) {
|
|
26104
|
+
const knownExpression = resolveKnownAttributeSource(bind.expr, (source) =>
|
|
26105
|
+
b.id(bag.local(`_prev$${source.binding.id}`)),
|
|
26106
|
+
);
|
|
26107
|
+
if (knownExpression !== bind.expr) bind = { ...bind, expr: knownExpression };
|
|
24679
26108
|
if (bind.deferred) return emitDeferredMount(bind, elVar, bag);
|
|
24680
26109
|
const org = bindingOrigin(bind);
|
|
24681
26110
|
const st = (node) => inheritOriginLoc(node, org);
|
|
@@ -24689,6 +26118,46 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
24689
26118
|
// name; everything else in the call maps to the value expression.
|
|
24690
26119
|
const callee = () => attrLoweringToken(b.id(`_$${attrBindingHelper(bind)}`), bind);
|
|
24691
26120
|
const nameLit = () => attrLoweringToken(b.literal(bind.name), bind);
|
|
26121
|
+
const signalHelper = directSignalBindingHelper(bind);
|
|
26122
|
+
if (signalHelper !== null) {
|
|
26123
|
+
const text = bind.kind === 'text' || bind.kind === 'textOnlyChild';
|
|
26124
|
+
let mount = b.call(
|
|
26125
|
+
attrLoweringToken(b.id(`_$${signalHelper}`), bind),
|
|
26126
|
+
...directSignalBindingArgs(bind, el(), undefinedNode(), text ? V() : bind.expr),
|
|
26127
|
+
);
|
|
26128
|
+
if (text) {
|
|
26129
|
+
// A possible handle is not a subscription. Primitive text mounts through
|
|
26130
|
+
// the canonical hydration/seeded-placeholder writer, without journaling
|
|
26131
|
+
// a freshly cloned Text as though it were an existing update target.
|
|
26132
|
+
const scalarMount =
|
|
26133
|
+
bind.kind === 'textOnlyChild'
|
|
26134
|
+
? b.call('_$htext', el(), V(), ...(bind.seededText ? [b.literal(1)] : []))
|
|
26135
|
+
: bind.bindingMarker
|
|
26136
|
+
? b.call('_$bindingText', el(), V(), b.literal(bind.bindingMarker))
|
|
26137
|
+
: b.call('_$htextSwap', el(), V());
|
|
26138
|
+
mount = b.conditional(
|
|
26139
|
+
b.logical(
|
|
26140
|
+
'||',
|
|
26141
|
+
b.binary('===', V(), b.literal(null)),
|
|
26142
|
+
b.logical(
|
|
26143
|
+
'&&',
|
|
26144
|
+
b.binary('!==', b.unary('typeof', V()), b.literal('object')),
|
|
26145
|
+
b.binary('!==', b.unary('typeof', V()), b.literal('function')),
|
|
26146
|
+
),
|
|
26147
|
+
),
|
|
26148
|
+
scalarMount,
|
|
26149
|
+
mount,
|
|
26150
|
+
);
|
|
26151
|
+
}
|
|
26152
|
+
return st(
|
|
26153
|
+
b.block([
|
|
26154
|
+
...mountHost(),
|
|
26155
|
+
...(text ? [b.const('_v', bind.expr)] : []),
|
|
26156
|
+
b.stmt(b.assignment('=', local(`_sig$${bind.id}`), mount)),
|
|
26157
|
+
...(text ? [b.stmt(b.assignment('=', local(`_prev$${bind.id}`), V()))] : []),
|
|
26158
|
+
]),
|
|
26159
|
+
);
|
|
26160
|
+
}
|
|
24692
26161
|
// `suppressHydrationWarning`: stamp a JS flag (NOT a DOM attribute) the runtime reads to
|
|
24693
26162
|
// keep the server value + skip the warning on a hydration mismatch for this element.
|
|
24694
26163
|
if (bind.kind === 'suppress') {
|
|
@@ -24714,11 +26183,34 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
24714
26183
|
if (bind.kind === 'formDiagnostic') {
|
|
24715
26184
|
const args = [el(), b.literal(bind.diagnosticKind)];
|
|
24716
26185
|
if (bind.selectedBinding !== undefined) {
|
|
24717
|
-
|
|
26186
|
+
const selected = bind.selectedBinding;
|
|
26187
|
+
args.push(
|
|
26188
|
+
b.arrow(
|
|
26189
|
+
[],
|
|
26190
|
+
bagFieldNode(bag, `${selected.signalDirect ? '_sig' : '_prev'}$${selected.id}`),
|
|
26191
|
+
),
|
|
26192
|
+
);
|
|
24718
26193
|
}
|
|
24719
26194
|
return st(b.stmt(b.call('_$queueFormAuthoringDiagnostic', ...args)));
|
|
24720
26195
|
}
|
|
24721
26196
|
switch (bind.kind) {
|
|
26197
|
+
case 'nativeProjection': {
|
|
26198
|
+
return st(
|
|
26199
|
+
b.block([
|
|
26200
|
+
...mountHost(),
|
|
26201
|
+
b.stmt(
|
|
26202
|
+
b.call(
|
|
26203
|
+
bind.helper,
|
|
26204
|
+
b.id('__s'),
|
|
26205
|
+
b.literal(bind.slotIndex),
|
|
26206
|
+
el(),
|
|
26207
|
+
b.arrow([], bind.expr),
|
|
26208
|
+
b.array(bind.fields.map((field) => b.literal(field))),
|
|
26209
|
+
),
|
|
26210
|
+
),
|
|
26211
|
+
]),
|
|
26212
|
+
);
|
|
26213
|
+
}
|
|
24722
26214
|
case 'nativeStyle': {
|
|
24723
26215
|
return st(
|
|
24724
26216
|
b.block([
|
|
@@ -24800,6 +26292,29 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
24800
26292
|
const sources = commitSourceRows(bind.sources, (binding, spread) =>
|
|
24801
26293
|
b.id(bag.local(`${spread ? '_sp' : '_prev'}$${binding.id}`)),
|
|
24802
26294
|
);
|
|
26295
|
+
if (bind.signalHostSources) {
|
|
26296
|
+
return st(
|
|
26297
|
+
b.block([
|
|
26298
|
+
...hostMount,
|
|
26299
|
+
b.stmt(
|
|
26300
|
+
b.assignment(
|
|
26301
|
+
'=',
|
|
26302
|
+
propsLocal,
|
|
26303
|
+
presentationHostSourcesCall(
|
|
26304
|
+
bind,
|
|
26305
|
+
b.id('__s'),
|
|
26306
|
+
undefinedNode(),
|
|
26307
|
+
el(),
|
|
26308
|
+
sources,
|
|
26309
|
+
b.literal(bind.signalSite, JSON.stringify(bind.signalSite)),
|
|
26310
|
+
b.literal(bind.hasNestedChildren === true),
|
|
26311
|
+
...(bind.readStyle ? [b.id(bind.readStyle)] : []),
|
|
26312
|
+
),
|
|
26313
|
+
),
|
|
26314
|
+
),
|
|
26315
|
+
]),
|
|
26316
|
+
);
|
|
26317
|
+
}
|
|
24803
26318
|
// The cleanup closure reads the bag through the captured `_b` — the bag
|
|
24804
26319
|
// exists by the time any cleanup runs (committed at mount end), and the
|
|
24805
26320
|
// `_host$` field is re-written by updates, so the read must be live.
|
|
@@ -24844,7 +26359,18 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
24844
26359
|
return st(
|
|
24845
26360
|
b.block([
|
|
24846
26361
|
b.const('_v', bind.expr),
|
|
24847
|
-
b.stmt(
|
|
26362
|
+
b.stmt(
|
|
26363
|
+
b.assignment(
|
|
26364
|
+
'=',
|
|
26365
|
+
local(`_txt$${bind.id}`),
|
|
26366
|
+
b.call(
|
|
26367
|
+
bind.bindingMarker ? '_$bindingText' : '_$htextSwap',
|
|
26368
|
+
el(),
|
|
26369
|
+
V(),
|
|
26370
|
+
...(bind.bindingMarker ? [b.literal(bind.bindingMarker)] : []),
|
|
26371
|
+
),
|
|
26372
|
+
),
|
|
26373
|
+
),
|
|
24848
26374
|
b.stmt(b.assignment('=', local(`_prev$${bind.id}`), V())),
|
|
24849
26375
|
]),
|
|
24850
26376
|
);
|
|
@@ -25100,6 +26626,10 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
25100
26626
|
}
|
|
25101
26627
|
|
|
25102
26628
|
function emitBindingUpdate(bind, bag, inlineBindingGuards = false) {
|
|
26629
|
+
const knownExpression = resolveKnownAttributeSource(bind.expr, (source) =>
|
|
26630
|
+
bagFieldNode(bag, `_prev$${source.binding.id}`),
|
|
26631
|
+
);
|
|
26632
|
+
if (knownExpression !== bind.expr) bind = { ...bind, expr: knownExpression };
|
|
25103
26633
|
if (bind.mountOnly) return null;
|
|
25104
26634
|
const org = bindingOrigin(bind);
|
|
25105
26635
|
const st = (node) => inheritOriginLoc(node, org);
|
|
@@ -25112,7 +26642,74 @@ function emitBindingUpdate(bind, bag, inlineBindingGuards = false) {
|
|
|
25112
26642
|
const callee = () =>
|
|
25113
26643
|
attrLoweringToken(b.id(`_$${attrBindingUpdateHelper(bind, inlineBindingGuards)}`), bind);
|
|
25114
26644
|
const nameLit = () => attrLoweringToken(b.literal(bind.name), bind);
|
|
26645
|
+
const signalHelper = directSignalBindingHelper(bind);
|
|
26646
|
+
if (signalHelper !== null) {
|
|
26647
|
+
if (bind.kind === 'text' || bind.kind === 'textOnlyChild') {
|
|
26648
|
+
return st(
|
|
26649
|
+
b.block([
|
|
26650
|
+
b.const('_v', bind.expr),
|
|
26651
|
+
// Repeated primitives need neither a binding read nor a bag write.
|
|
26652
|
+
// Objects/functions still re-enter: an unchanged handle can have
|
|
26653
|
+
// changed value, pending, or error state since the last render.
|
|
26654
|
+
b.if(
|
|
26655
|
+
b.logical(
|
|
26656
|
+
'||',
|
|
26657
|
+
b.binary('!==', F('_prev'), V()),
|
|
26658
|
+
b.logical(
|
|
26659
|
+
'&&',
|
|
26660
|
+
b.binary('!==', V(), b.literal(null)),
|
|
26661
|
+
b.logical(
|
|
26662
|
+
'||',
|
|
26663
|
+
b.binary('===', b.unary('typeof', V()), b.literal('object')),
|
|
26664
|
+
b.binary('===', b.unary('typeof', V()), b.literal('function')),
|
|
26665
|
+
),
|
|
26666
|
+
),
|
|
26667
|
+
),
|
|
26668
|
+
b.block([
|
|
26669
|
+
b.stmt(
|
|
26670
|
+
b.assignment(
|
|
26671
|
+
'=',
|
|
26672
|
+
F('_sig'),
|
|
26673
|
+
b.call(
|
|
26674
|
+
b.id(`_$${signalHelper}`),
|
|
26675
|
+
...directSignalBindingArgs(bind, F('_el'), F('_sig'), V(), F('_prev')),
|
|
26676
|
+
),
|
|
26677
|
+
),
|
|
26678
|
+
),
|
|
26679
|
+
b.stmt(b.assignment('=', F('_prev'), V())),
|
|
26680
|
+
]),
|
|
26681
|
+
),
|
|
26682
|
+
]),
|
|
26683
|
+
);
|
|
26684
|
+
}
|
|
26685
|
+
return st(
|
|
26686
|
+
b.stmt(
|
|
26687
|
+
b.assignment(
|
|
26688
|
+
'=',
|
|
26689
|
+
F('_sig'),
|
|
26690
|
+
b.call(
|
|
26691
|
+
attrLoweringToken(b.id(`_$${signalHelper}`), bind),
|
|
26692
|
+
...directSignalBindingArgs(bind, F('_el'), F('_sig')),
|
|
26693
|
+
),
|
|
26694
|
+
),
|
|
26695
|
+
),
|
|
26696
|
+
);
|
|
26697
|
+
}
|
|
25115
26698
|
switch (bind.kind) {
|
|
26699
|
+
case 'nativeProjection': {
|
|
26700
|
+
return st(
|
|
26701
|
+
b.stmt(
|
|
26702
|
+
b.call(
|
|
26703
|
+
bind.helper,
|
|
26704
|
+
b.id('__s'),
|
|
26705
|
+
b.literal(bind.slotIndex),
|
|
26706
|
+
F('_el'),
|
|
26707
|
+
b.arrow([], bind.expr),
|
|
26708
|
+
b.array(bind.fields.map((field) => b.literal(field))),
|
|
26709
|
+
),
|
|
26710
|
+
),
|
|
26711
|
+
);
|
|
26712
|
+
}
|
|
25116
26713
|
case 'nativeStyle': {
|
|
25117
26714
|
return st(
|
|
25118
26715
|
b.stmt(b.call(bind.helper, b.id('__s'), b.literal(bind.slotIndex), F('_el'), bind.expr)),
|
|
@@ -25187,6 +26784,26 @@ function emitBindingUpdate(bind, bag, inlineBindingGuards = false) {
|
|
|
25187
26784
|
const sources = commitSourceRows(bind.sources, (binding, spread) =>
|
|
25188
26785
|
bagFieldNode(bag, `${spread ? '_sp' : '_prev'}$${binding.id}`),
|
|
25189
26786
|
);
|
|
26787
|
+
if (bind.signalHostSources) {
|
|
26788
|
+
return st(
|
|
26789
|
+
b.stmt(
|
|
26790
|
+
b.assignment(
|
|
26791
|
+
'=',
|
|
26792
|
+
F('_host'),
|
|
26793
|
+
presentationHostSourcesCall(
|
|
26794
|
+
bind,
|
|
26795
|
+
b.id('__s'),
|
|
26796
|
+
F('_host'),
|
|
26797
|
+
F('_el'),
|
|
26798
|
+
sources,
|
|
26799
|
+
b.literal(bind.signalSite, JSON.stringify(bind.signalSite)),
|
|
26800
|
+
b.literal(bind.hasNestedChildren === true),
|
|
26801
|
+
...(bind.readStyle ? [b.id(bind.readStyle)] : []),
|
|
26802
|
+
),
|
|
26803
|
+
),
|
|
26804
|
+
),
|
|
26805
|
+
);
|
|
26806
|
+
}
|
|
25190
26807
|
return st(
|
|
25191
26808
|
b.stmt(
|
|
25192
26809
|
b.assignment(
|
|
@@ -25555,206 +27172,8 @@ function emitBindingUpdate(bind, bag, inlineBindingGuards = false) {
|
|
|
25555
27172
|
// HTML emission
|
|
25556
27173
|
// ===========================================================================
|
|
25557
27174
|
|
|
25558
|
-
|
|
25559
|
-
|
|
25560
|
-
// runtime DATA, not JavaScript syntax: the browser-facing `template(html)` ABI
|
|
25561
|
-
// intentionally stays a string. The compiler represents that data as an
|
|
25562
|
-
// structured tree of elements and typed data chunks until allocTemplate
|
|
25563
|
-
// serializes the completed template exactly once. Child templates compose by
|
|
25564
|
-
// transferring nodes, so no intermediate HTML is joined and no origin offsets
|
|
25565
|
-
// need to be shifted during construction. Origin spans remain local to their
|
|
25566
|
-
// chunk; the one serializer shifts them into final logical-HTML coordinates.
|
|
25567
|
-
//
|
|
25568
|
-
// Origin collection stays gated on `ctx.inspect`. The normal compile path pays
|
|
25569
|
-
// for the template chunks (the structural representation replacing string
|
|
25570
|
-
// concatenation) but not for source-span arrays.
|
|
25571
|
-
// ---------------------------------------------------------------------------
|
|
25572
|
-
|
|
25573
|
-
/**
|
|
25574
|
-
* @typedef {{ start: number, end: number, srcStart: number, srcEnd: number,
|
|
25575
|
-
* kind: 'tag-open' | 'tag-close' | 'attr-name' | 'attr-value' | 'text' }} TemplateOrigin
|
|
25576
|
-
*/
|
|
25577
|
-
|
|
25578
|
-
/**
|
|
25579
|
-
* @typedef {'syntax' | 'tag-open' | 'tag-close' | 'attribute' | 'text' |
|
|
25580
|
-
* 'anchor' | 'fragment-open' | 'fragment-close' | 'raw'} TemplatePartKind
|
|
25581
|
-
*/
|
|
25582
|
-
|
|
25583
|
-
/**
|
|
25584
|
-
* @typedef {{
|
|
25585
|
-
* type: 'TemplatePart',
|
|
25586
|
-
* kind: TemplatePartKind,
|
|
25587
|
-
* value: string,
|
|
25588
|
-
* origins: TemplateOrigin[] | null,
|
|
25589
|
-
* length: number,
|
|
25590
|
-
* }} TemplatePart
|
|
25591
|
-
*/
|
|
25592
|
-
|
|
25593
|
-
/**
|
|
25594
|
-
* @typedef {{
|
|
25595
|
-
* type: 'TemplateElement',
|
|
25596
|
-
* tag: string,
|
|
25597
|
-
* namespace: string,
|
|
25598
|
-
* synthetic: boolean,
|
|
25599
|
-
* opening: TemplatePart,
|
|
25600
|
-
* attributes: TemplatePart[],
|
|
25601
|
-
* openingEnd: TemplatePart,
|
|
25602
|
-
* children: TemplateNode[],
|
|
25603
|
-
* closing: TemplatePart | null,
|
|
25604
|
-
* length: number,
|
|
25605
|
-
* }} TemplateElement
|
|
25606
|
-
*/
|
|
25607
|
-
|
|
25608
|
-
/** @typedef {TemplatePart | TemplateElement} TemplateNode */
|
|
25609
|
-
|
|
25610
|
-
/**
|
|
25611
|
-
* @typedef {{
|
|
25612
|
-
* type: 'Template',
|
|
25613
|
-
* parts: TemplateNode[],
|
|
25614
|
-
* length: number,
|
|
25615
|
-
* }} TemplateIR
|
|
25616
|
-
*/
|
|
25617
|
-
|
|
25618
|
-
/** @returns {TemplateIR} */
|
|
25619
|
-
function createTemplateIr() {
|
|
25620
|
-
return { type: 'Template', parts: [], length: 0 };
|
|
25621
|
-
}
|
|
25622
|
-
|
|
25623
|
-
/**
|
|
25624
|
-
* @param {string} value
|
|
25625
|
-
* @param {TemplatePartKind} [kind]
|
|
25626
|
-
* @param {TemplateOrigin[] | null} [origins]
|
|
25627
|
-
* @returns {TemplatePart}
|
|
25628
|
-
*/
|
|
25629
|
-
function createTemplatePart(value, kind = 'raw', origins = null) {
|
|
25630
|
-
return { type: 'TemplatePart', kind, value, origins, length: value.length };
|
|
25631
|
-
}
|
|
25632
|
-
|
|
25633
|
-
/** @param {TemplateIR} template @param {TemplateNode} node */
|
|
25634
|
-
function appendTemplateNode(template, node) {
|
|
25635
|
-
template.parts.push(node);
|
|
25636
|
-
template.length += node.length;
|
|
25637
|
-
}
|
|
25638
|
-
|
|
25639
|
-
/**
|
|
25640
|
-
* @param {TemplateIR} template
|
|
25641
|
-
* @param {string} value
|
|
25642
|
-
* @param {TemplatePartKind} [kind]
|
|
25643
|
-
* @param {TemplateOrigin[] | null} [origins]
|
|
25644
|
-
*/
|
|
25645
|
-
function appendTemplatePart(template, value, kind = 'raw', origins = null) {
|
|
25646
|
-
if (value === '') return;
|
|
25647
|
-
appendTemplateNode(template, createTemplatePart(value, kind, origins));
|
|
25648
|
-
}
|
|
25649
|
-
|
|
25650
|
-
/** @param {TemplateIR} into @param {TemplateIR} child */
|
|
25651
|
-
function appendTemplateIr(into, child) {
|
|
25652
|
-
if (child.length === 0) return;
|
|
25653
|
-
for (const part of child.parts) into.parts.push(part);
|
|
25654
|
-
into.length += child.length;
|
|
25655
|
-
}
|
|
25656
|
-
|
|
25657
|
-
/**
|
|
25658
|
-
* @param {string} value
|
|
25659
|
-
* @param {TemplatePartKind} [kind]
|
|
25660
|
-
* @param {TemplateOrigin[] | null} [origins]
|
|
25661
|
-
* @returns {TemplateIR}
|
|
25662
|
-
*/
|
|
25663
|
-
function templatePart(value, kind = 'raw', origins = null) {
|
|
25664
|
-
const template = createTemplateIr();
|
|
25665
|
-
appendTemplatePart(template, value, kind, origins);
|
|
25666
|
-
return template;
|
|
25667
|
-
}
|
|
25668
|
-
|
|
25669
|
-
/**
|
|
25670
|
-
* @param {string} tag
|
|
25671
|
-
* @param {string} namespace
|
|
25672
|
-
* @param {boolean} voidElement
|
|
25673
|
-
* @param {TemplateIR} attributes
|
|
25674
|
-
* @param {TemplateIR} children
|
|
25675
|
-
* @param {TemplateOrigin[] | null} openOrigins
|
|
25676
|
-
* @param {TemplateOrigin[] | null} closeOrigins
|
|
25677
|
-
* @param {boolean} [synthetic]
|
|
25678
|
-
* @returns {TemplateIR}
|
|
25679
|
-
*/
|
|
25680
|
-
function templateElement(
|
|
25681
|
-
tag,
|
|
25682
|
-
namespace,
|
|
25683
|
-
voidElement,
|
|
25684
|
-
attributes,
|
|
25685
|
-
children,
|
|
25686
|
-
openOrigins,
|
|
25687
|
-
closeOrigins,
|
|
25688
|
-
synthetic = false,
|
|
25689
|
-
) {
|
|
25690
|
-
const opening = createTemplatePart(`<${tag}`, 'tag-open', openOrigins);
|
|
25691
|
-
const openingEnd = createTemplatePart(voidElement ? '/>' : '>', 'syntax');
|
|
25692
|
-
const closing = voidElement ? null : createTemplatePart(`</${tag}>`, 'tag-close', closeOrigins);
|
|
25693
|
-
const node = {
|
|
25694
|
-
type: 'TemplateElement',
|
|
25695
|
-
tag,
|
|
25696
|
-
namespace,
|
|
25697
|
-
synthetic,
|
|
25698
|
-
opening,
|
|
25699
|
-
attributes: /** @type {TemplatePart[]} */ (attributes.parts),
|
|
25700
|
-
openingEnd,
|
|
25701
|
-
children: children.parts,
|
|
25702
|
-
closing,
|
|
25703
|
-
length:
|
|
25704
|
-
opening.value.length +
|
|
25705
|
-
attributes.length +
|
|
25706
|
-
openingEnd.value.length +
|
|
25707
|
-
children.length +
|
|
25708
|
-
(closing === null ? 0 : closing.value.length),
|
|
25709
|
-
};
|
|
25710
|
-
const template = createTemplateIr();
|
|
25711
|
-
appendTemplateNode(template, node);
|
|
25712
|
-
return template;
|
|
25713
|
-
}
|
|
25714
|
-
|
|
25715
|
-
/**
|
|
25716
|
-
* Serialize the completed template once. The IR already contains the exact
|
|
25717
|
-
* runtime bytes; this pass only joins them and converts chunk-local origins to
|
|
25718
|
-
* final logical-HTML offsets.
|
|
25719
|
-
*
|
|
25720
|
-
* @param {TemplateIR} template
|
|
25721
|
-
*/
|
|
25722
|
-
function serializeTemplateIr(template) {
|
|
25723
|
-
const values = [];
|
|
25724
|
-
let origins = null;
|
|
25725
|
-
let offset = 0;
|
|
25726
|
-
const appendPart = (part) => {
|
|
25727
|
-
values.push(part.value);
|
|
25728
|
-
if (part.origins !== null) {
|
|
25729
|
-
origins ??= [];
|
|
25730
|
-
for (const origin of part.origins) {
|
|
25731
|
-
origins.push({
|
|
25732
|
-
start: origin.start + offset,
|
|
25733
|
-
end: origin.end + offset,
|
|
25734
|
-
srcStart: origin.srcStart,
|
|
25735
|
-
srcEnd: origin.srcEnd,
|
|
25736
|
-
kind: origin.kind,
|
|
25737
|
-
});
|
|
25738
|
-
}
|
|
25739
|
-
}
|
|
25740
|
-
offset += part.value.length;
|
|
25741
|
-
};
|
|
25742
|
-
const visit = (node) => {
|
|
25743
|
-
if (node.type === 'TemplatePart') {
|
|
25744
|
-
appendPart(node);
|
|
25745
|
-
return;
|
|
25746
|
-
}
|
|
25747
|
-
appendPart(node.opening);
|
|
25748
|
-
for (const attribute of node.attributes) appendPart(attribute);
|
|
25749
|
-
appendPart(node.openingEnd);
|
|
25750
|
-
for (const child of node.children) visit(child);
|
|
25751
|
-
if (node.closing !== null) appendPart(node.closing);
|
|
25752
|
-
};
|
|
25753
|
-
for (const node of template.parts) {
|
|
25754
|
-
visit(node);
|
|
25755
|
-
}
|
|
25756
|
-
return { html: values.join(''), origins };
|
|
25757
|
-
}
|
|
27175
|
+
/** @typedef {import('./template-ir.js').TemplateIR} TemplateIR */
|
|
27176
|
+
/** @typedef {import('./template-ir.js').TemplateOrigin} TemplateOrigin */
|
|
25758
27177
|
|
|
25759
27178
|
// Record the origin entries for one static-attribute chunk appended at `base`
|
|
25760
27179
|
// (a bakeStaticAttr result or the inline ` style="…"` bakes, which share the
|
|
@@ -25826,15 +27245,36 @@ function emitNodeHtml(
|
|
|
25826
27245
|
parentNs = 'html',
|
|
25827
27246
|
cssHash = null,
|
|
25828
27247
|
) {
|
|
27248
|
+
if (node._octaneBindingValue || node._octaneBindingSlot) {
|
|
27249
|
+
const ch = makeChildCall(node.expression, ctx, componentName, inlinedSubs, cssHash, parentNs);
|
|
27250
|
+
ch.bindingMarker = bindingChildMarker(node);
|
|
27251
|
+
ch.hostPath = path.slice(0, -1);
|
|
27252
|
+
ch.anchorPath = path;
|
|
27253
|
+
compCalls.push(ch);
|
|
27254
|
+
return templatePart('<!>', 'anchor');
|
|
27255
|
+
}
|
|
25829
27256
|
if (node.type === 'Text') {
|
|
25830
|
-
if (
|
|
25831
|
-
|
|
25832
|
-
|
|
25833
|
-
|
|
25834
|
-
|
|
25835
|
-
|
|
25836
|
-
|
|
25837
|
-
|
|
27257
|
+
if (
|
|
27258
|
+
isDirectSignalHandleExpression(node.expression) ||
|
|
27259
|
+
isKnownTextChildExpression(node.expression, ctx.knownStringChildLocals)
|
|
27260
|
+
) {
|
|
27261
|
+
bindings.push(
|
|
27262
|
+
markDirectSignalBinding(
|
|
27263
|
+
{
|
|
27264
|
+
id: bindings.length,
|
|
27265
|
+
kind: 'text',
|
|
27266
|
+
expr: resolveStyleExpr(node.expression, cssHash),
|
|
27267
|
+
bindingMarker: node._octaneBindingText
|
|
27268
|
+
? bindingMarker(node._octaneBindingText, 't')
|
|
27269
|
+
: undefined,
|
|
27270
|
+
path: path.slice(0, -1),
|
|
27271
|
+
childIndex: path[path.length - 1],
|
|
27272
|
+
},
|
|
27273
|
+
ctx,
|
|
27274
|
+
node.expression,
|
|
27275
|
+
'binding',
|
|
27276
|
+
),
|
|
27277
|
+
);
|
|
25838
27278
|
return templatePart('<!>', 'anchor');
|
|
25839
27279
|
}
|
|
25840
27280
|
// Bare `{expr}` (no string cast) → RENDERABLE hole at a top-level / multi-
|
|
@@ -25853,6 +27293,28 @@ function emitNodeHtml(
|
|
|
25853
27293
|
// cannot represent a Fragment ref or template-only control flow in that body.
|
|
25854
27294
|
// Ordinary root portals keep their existing descriptor/childSlot lowering.
|
|
25855
27295
|
if (node.type === 'TSRXExpression') {
|
|
27296
|
+
if (isDirectSignalHandleExpression(node.expression)) {
|
|
27297
|
+
bindings.push(
|
|
27298
|
+
markDirectSignalBinding(
|
|
27299
|
+
{
|
|
27300
|
+
id: bindings.length,
|
|
27301
|
+
kind: 'text',
|
|
27302
|
+
expr: tsrxExprNode(
|
|
27303
|
+
resolveStyleExpr(node.expression, cssHash),
|
|
27304
|
+
ctx,
|
|
27305
|
+
componentName,
|
|
27306
|
+
inlinedSubs,
|
|
27307
|
+
),
|
|
27308
|
+
path: path.slice(0, -1),
|
|
27309
|
+
childIndex: path[path.length - 1],
|
|
27310
|
+
},
|
|
27311
|
+
ctx,
|
|
27312
|
+
node.expression,
|
|
27313
|
+
'binding',
|
|
27314
|
+
),
|
|
27315
|
+
);
|
|
27316
|
+
return templatePart('<!>', 'anchor');
|
|
27317
|
+
}
|
|
25856
27318
|
const fragmentBindings = ctx._fragRefStack;
|
|
25857
27319
|
if (
|
|
25858
27320
|
isCreatePortalCall(node.expression) &&
|
|
@@ -26162,7 +27624,9 @@ function emitElementHtml(
|
|
|
26162
27624
|
const hostNs = nsForSelf(tag, parentNs);
|
|
26163
27625
|
const childNs = nsForChildren(tag, parentNs);
|
|
26164
27626
|
// Collect attributes.
|
|
26165
|
-
const attrs =
|
|
27627
|
+
const attrs = expandKnownAttributeSpreads(
|
|
27628
|
+
node.attributes || node.openingElement?.attributes || [],
|
|
27629
|
+
);
|
|
26166
27630
|
// A null/undefined child alongside direct raw HTML is semantically absent.
|
|
26167
27631
|
// Suppress its child binding so hydration cannot clear the raw HTML that the
|
|
26168
27632
|
// preceding binding just adopted/applied.
|
|
@@ -26178,7 +27642,7 @@ function emitElementHtml(
|
|
|
26178
27642
|
// into the template HTML (the spread would clobber them at runtime) —
|
|
26179
27643
|
// emit them as bindings in source order instead.
|
|
26180
27644
|
const firstSpreadIdx = attrs.findIndex(
|
|
26181
|
-
(a) => a.type === 'SpreadAttribute' || a.type === 'JSXSpreadAttribute',
|
|
27645
|
+
(a) => !a.knownSource && (a.type === 'SpreadAttribute' || a.type === 'JSXSpreadAttribute'),
|
|
26182
27646
|
);
|
|
26183
27647
|
const directPropIdentities = new Set();
|
|
26184
27648
|
let hasDuplicateDirectProp = false;
|
|
@@ -26213,11 +27677,21 @@ function emitElementHtml(
|
|
|
26213
27677
|
(tag === 'select' &&
|
|
26214
27678
|
directPropNames.has('multiple') &&
|
|
26215
27679
|
(directPropNames.has('value') || directPropNames.has('defaultValue')));
|
|
27680
|
+
const hasDirectSignalStyle =
|
|
27681
|
+
!ctx.nativeReads &&
|
|
27682
|
+
attrs.some((attr) => {
|
|
27683
|
+
if (attr.type !== 'Attribute' && attr.type !== 'JSXAttribute') return false;
|
|
27684
|
+
if (normalizeJsxAttrName(jsxAttrRawName(attr), tag, hostNs) !== 'style') return false;
|
|
27685
|
+
const value = attr.value;
|
|
27686
|
+
const inner = value?.type === 'JSXExpressionContainer' ? value.expression : value;
|
|
27687
|
+
return spreadContainsDirectSignalHandle(inner);
|
|
27688
|
+
});
|
|
26216
27689
|
const resolveHostPropsAcrossSources =
|
|
26217
27690
|
firstSpreadIdx !== -1 ||
|
|
26218
27691
|
hasDuplicateDirectProp ||
|
|
26219
27692
|
hasDirectFormCascade ||
|
|
26220
|
-
resolveDevCustomizedHost
|
|
27693
|
+
resolveDevCustomizedHost ||
|
|
27694
|
+
hasDirectSignalStyle;
|
|
26221
27695
|
const hostClientSources = [];
|
|
26222
27696
|
let directChildrenClientBinding = null;
|
|
26223
27697
|
let hostCommitClientBinding = null;
|
|
@@ -26258,9 +27732,45 @@ function emitElementHtml(
|
|
|
26258
27732
|
typeWriters.length === 1 &&
|
|
26259
27733
|
(staticInputType === 'checkbox' || staticInputType === 'radio');
|
|
26260
27734
|
const attrTemplate = createTemplateIr();
|
|
27735
|
+
let directSignalControlSite = null;
|
|
27736
|
+
const ensureDirectControlSite = (site) => {
|
|
27737
|
+
if (directSignalControlSite !== null) return;
|
|
27738
|
+
if (directPropNames.has('data-octane-input')) {
|
|
27739
|
+
throw new Error(
|
|
27740
|
+
'`data-octane-input` is reserved for compiler-owned writable signal controls.',
|
|
27741
|
+
);
|
|
27742
|
+
}
|
|
27743
|
+
directSignalControlSite = site;
|
|
27744
|
+
appendTemplatePart(attrTemplate, ` data-octane-input="${escapeAttr(site)}"`, 'attribute', null);
|
|
27745
|
+
};
|
|
27746
|
+
const markDirectControl = (binding, expression, origin) => {
|
|
27747
|
+
if (!canCarryDirectSignalHandle(expression)) return binding;
|
|
27748
|
+
const site = directSignalSite(ctx, node, 'input');
|
|
27749
|
+
ensureDirectControlSite(site);
|
|
27750
|
+
ctx.signalBindingsUsed = true;
|
|
27751
|
+
ctx.signalBindingsEager = true;
|
|
27752
|
+
return { ...binding, signalDirect: true, signalSite: site };
|
|
27753
|
+
};
|
|
27754
|
+
const hostSignalSite = directSignalSite(ctx, node, 'binding');
|
|
27755
|
+
if (firstSpreadIdx !== -1 && (tag === 'input' || tag === 'textarea' || tag === 'select')) {
|
|
27756
|
+
ensureDirectControlSite(hostSignalSite);
|
|
27757
|
+
}
|
|
26261
27758
|
let sawRef = false;
|
|
26262
27759
|
for (let attrI = 0; attrI < attrs.length; attrI++) {
|
|
26263
27760
|
const attr = attrs[attrI];
|
|
27761
|
+
if (attr._octaneKnownProjectionSource) continue;
|
|
27762
|
+
if (attr.knownSource) {
|
|
27763
|
+
const binding = {
|
|
27764
|
+
id: bindings.length,
|
|
27765
|
+
kind: attr.knownSource.projection ? 'nativeProjection' : 'hostValue',
|
|
27766
|
+
expr: tsrxExprNode(attr.argument, ctx, componentName, inlinedSubs),
|
|
27767
|
+
fields: attr.knownSource.fields,
|
|
27768
|
+
path,
|
|
27769
|
+
};
|
|
27770
|
+
attr.knownSource.binding = binding;
|
|
27771
|
+
bindings.push(binding);
|
|
27772
|
+
continue;
|
|
27773
|
+
}
|
|
26264
27774
|
// `<div {...props}/>` — runtime spread. Emits one setSpread binding that
|
|
26265
27775
|
// routes each key (class / style / on… / attr / ref) and diffs against
|
|
26266
27776
|
// the prior spread object to clear removed keys.
|
|
@@ -26269,6 +27779,7 @@ function emitElementHtml(
|
|
|
26269
27779
|
const binding = {
|
|
26270
27780
|
id: bindings.length,
|
|
26271
27781
|
kind: 'hostSpread',
|
|
27782
|
+
...(attr._octaneBindingRest ? { bindingRest: attr._octaneBindingRest } : {}),
|
|
26272
27783
|
// The INNER spread argument; the emit re-derives the
|
|
26273
27784
|
// `_$snapshotSpread(…)` wrapper around it.
|
|
26274
27785
|
expr: tsrxExprNode(attr.argument, ctx, componentName, inlinedSubs),
|
|
@@ -26281,6 +27792,24 @@ function emitElementHtml(
|
|
|
26281
27792
|
}
|
|
26282
27793
|
if (attr.type !== 'Attribute' && attr.type !== 'JSXAttribute') continue;
|
|
26283
27794
|
const rawAttrName = jsxAttrRawName(attr);
|
|
27795
|
+
if (attr._octaneBindingClassGroups) {
|
|
27796
|
+
const group = attr._octaneBindingClassGroups;
|
|
27797
|
+
bindings.push({
|
|
27798
|
+
id: bindings.length,
|
|
27799
|
+
kind: 'attr',
|
|
27800
|
+
name: group.receipt,
|
|
27801
|
+
attributeHelper: 'setBindingClass',
|
|
27802
|
+
expr: tsrxExprNode(
|
|
27803
|
+
b.array([b.array(group.baseline), b.array(group.groups)]),
|
|
27804
|
+
ctx,
|
|
27805
|
+
componentName,
|
|
27806
|
+
inlinedSubs,
|
|
27807
|
+
),
|
|
27808
|
+
path,
|
|
27809
|
+
nameOrigin: attr.name,
|
|
27810
|
+
});
|
|
27811
|
+
continue;
|
|
27812
|
+
}
|
|
26284
27813
|
if (resolveHostPropsAcrossSources) {
|
|
26285
27814
|
if (rawAttrName === 'ref') {
|
|
26286
27815
|
if (sawRef) {
|
|
@@ -26320,6 +27849,13 @@ function emitElementHtml(
|
|
|
26320
27849
|
ctx.inspect,
|
|
26321
27850
|
);
|
|
26322
27851
|
}
|
|
27852
|
+
if (
|
|
27853
|
+
(tag === 'input' || tag === 'textarea' || tag === 'select') &&
|
|
27854
|
+
(rawAttrName === 'value' || (tag === 'input' && rawAttrName === 'checked')) &&
|
|
27855
|
+
isDirectSignalHandleExpression(inner)
|
|
27856
|
+
) {
|
|
27857
|
+
ensureDirectControlSite(hostSignalSite);
|
|
27858
|
+
}
|
|
26323
27859
|
expr = tsrxExprNode(inner, ctx, componentName, inlinedSubs);
|
|
26324
27860
|
}
|
|
26325
27861
|
const binding = {
|
|
@@ -26511,19 +28047,17 @@ function emitElementHtml(
|
|
|
26511
28047
|
resolveFormControlsAcrossSpreads &&
|
|
26512
28048
|
(ctlKind !== null || (tag === 'select' && attrName === 'multiple'))
|
|
26513
28049
|
) {
|
|
28050
|
+
const sourceExpr =
|
|
28051
|
+
val == null
|
|
28052
|
+
? b.literal(true, 'true', attr)
|
|
28053
|
+
: val.type === 'JSXExpressionContainer'
|
|
28054
|
+
? val.expression
|
|
28055
|
+
: val;
|
|
26514
28056
|
const binding = {
|
|
26515
28057
|
id: bindings.length,
|
|
26516
28058
|
kind: 'formValue',
|
|
26517
28059
|
name: attrName,
|
|
26518
|
-
expr:
|
|
26519
|
-
val == null
|
|
26520
|
-
? b.literal(true, 'true', attr)
|
|
26521
|
-
: tsrxExprNode(
|
|
26522
|
-
val.type === 'JSXExpressionContainer' ? val.expression : val,
|
|
26523
|
-
ctx,
|
|
26524
|
-
componentName,
|
|
26525
|
-
inlinedSubs,
|
|
26526
|
-
),
|
|
28060
|
+
expr: tsrxExprNode(sourceExpr, ctx, componentName, inlinedSubs),
|
|
26527
28061
|
path,
|
|
26528
28062
|
};
|
|
26529
28063
|
bindings.push(binding);
|
|
@@ -26541,22 +28075,28 @@ function emitElementHtml(
|
|
|
26541
28075
|
} else if (ctlKind === 'checked' && leanChecked) {
|
|
26542
28076
|
ctlKind = 'checkedCheckable';
|
|
26543
28077
|
}
|
|
26544
|
-
|
|
28078
|
+
const sourceExpr =
|
|
28079
|
+
val == null
|
|
28080
|
+
? b.literal(true, 'true', attr)
|
|
28081
|
+
: val.type === 'JSXExpressionContainer'
|
|
28082
|
+
? val.expression
|
|
28083
|
+
: val;
|
|
28084
|
+
const binding = {
|
|
26545
28085
|
id: bindings.length,
|
|
26546
28086
|
kind: ctlKind,
|
|
26547
|
-
expr:
|
|
26548
|
-
val == null
|
|
26549
|
-
? b.literal(true, 'true', attr)
|
|
26550
|
-
: tsrxExprNode(
|
|
26551
|
-
val.type === 'JSXExpressionContainer' ? val.expression : val,
|
|
26552
|
-
ctx,
|
|
26553
|
-
componentName,
|
|
26554
|
-
inlinedSubs,
|
|
26555
|
-
),
|
|
28087
|
+
expr: tsrxExprNode(sourceExpr, ctx, componentName, inlinedSubs),
|
|
26556
28088
|
path,
|
|
26557
28089
|
ns: hostNs,
|
|
26558
28090
|
nameOrigin: attr.name,
|
|
26559
|
-
}
|
|
28091
|
+
};
|
|
28092
|
+
bindings.push(
|
|
28093
|
+
ctlKind === 'value' ||
|
|
28094
|
+
ctlKind === 'selectValue' ||
|
|
28095
|
+
ctlKind === 'checked' ||
|
|
28096
|
+
ctlKind === 'checkedCheckable'
|
|
28097
|
+
? markDirectControl(binding, sourceExpr, attr)
|
|
28098
|
+
: binding,
|
|
28099
|
+
);
|
|
26560
28100
|
continue;
|
|
26561
28101
|
}
|
|
26562
28102
|
// `autoFocus` never bakes/writes an attribute — React parity: the
|
|
@@ -26613,6 +28153,13 @@ function emitElementHtml(
|
|
|
26613
28153
|
// template (unless we're after a spread, which would clobber it); a fixed
|
|
26614
28154
|
// literal prefix plus dynamic suffix can avoid a whole-object diff.
|
|
26615
28155
|
if (attrName === 'style') {
|
|
28156
|
+
if (!ctx.nativeReads && isDirectSignalHandleExpression(inner)) {
|
|
28157
|
+
throw new Error(
|
|
28158
|
+
'Direct signal style objects cannot install property-targeted bindings. ' +
|
|
28159
|
+
'Use fixed style properties such as `style={{ color: color$ }}`, or use `.get()` ' +
|
|
28160
|
+
'for a one-way whole-style value.',
|
|
28161
|
+
);
|
|
28162
|
+
}
|
|
26616
28163
|
if (!isAfterSpread && inner.type === 'Literal' && typeof inner.value === 'string') {
|
|
26617
28164
|
const chunk = ` style="${escapeAttr(inner.value)}"`;
|
|
26618
28165
|
appendBakedAttribute(attrTemplate, chunk, attrName, attr.name, inner, ctx.inspect);
|
|
@@ -26837,7 +28384,7 @@ function emitElementHtml(
|
|
|
26837
28384
|
} else if (attrName === 'class') {
|
|
26838
28385
|
// (`className` was already normalized to `class` above.)
|
|
26839
28386
|
const scalar = firstSpreadIdx === -1 ? scalarStateClass(inner, ctx) : null;
|
|
26840
|
-
|
|
28387
|
+
const binding = {
|
|
26841
28388
|
id: bindings.length,
|
|
26842
28389
|
kind: 'class',
|
|
26843
28390
|
expr: scalar === null ? expr : tsrxExprNode(scalar, ctx, componentName, inlinedSubs),
|
|
@@ -26845,7 +28392,9 @@ function emitElementHtml(
|
|
|
26845
28392
|
ns: hostNs,
|
|
26846
28393
|
fresh: scalar === null && isFreshBindingExpr(inner),
|
|
26847
28394
|
nameOrigin: attr.name,
|
|
26848
|
-
|
|
28395
|
+
name: 'class',
|
|
28396
|
+
};
|
|
28397
|
+
bindings.push(markDirectSignalBinding(binding, ctx, inner, 'binding'));
|
|
26849
28398
|
} else if (
|
|
26850
28399
|
(tag === 'form' && attrName === 'action') ||
|
|
26851
28400
|
((tag === 'button' || tag === 'input') &&
|
|
@@ -26870,15 +28419,22 @@ function emitElementHtml(
|
|
|
26870
28419
|
// namespace, URL, controlled-form, and validity routing. Their value
|
|
26871
28420
|
// contract is uniform across native/custom HTML, SVG, and MathML:
|
|
26872
28421
|
// nullish removes and everything else stringifies.
|
|
26873
|
-
bindings.push(
|
|
26874
|
-
|
|
26875
|
-
|
|
26876
|
-
|
|
26877
|
-
|
|
26878
|
-
|
|
26879
|
-
|
|
26880
|
-
|
|
26881
|
-
|
|
28422
|
+
bindings.push(
|
|
28423
|
+
markDirectSignalBinding(
|
|
28424
|
+
{
|
|
28425
|
+
id: bindings.length,
|
|
28426
|
+
kind: 'ariaAttr',
|
|
28427
|
+
name: attrName,
|
|
28428
|
+
expr,
|
|
28429
|
+
path,
|
|
28430
|
+
ns: hostNs,
|
|
28431
|
+
nameOrigin: attr.name,
|
|
28432
|
+
},
|
|
28433
|
+
ctx,
|
|
28434
|
+
inner,
|
|
28435
|
+
'binding',
|
|
28436
|
+
),
|
|
28437
|
+
);
|
|
26882
28438
|
} else if (
|
|
26883
28439
|
!((hostNs === 'html' || hostNs === 'opaque') && tag.includes('-')) &&
|
|
26884
28440
|
BOOLEAN_ATTR_PROPS.has(attrName.toLowerCase())
|
|
@@ -26886,15 +28442,22 @@ function emitElementHtml(
|
|
|
26886
28442
|
// Exact native boolean props all share the canonical presence contract.
|
|
26887
28443
|
// The lowercase name is the final DOM spelling (including camelCase JSX
|
|
26888
28444
|
// aliases such as allowFullScreen -> allowfullscreen).
|
|
26889
|
-
bindings.push(
|
|
26890
|
-
|
|
26891
|
-
|
|
26892
|
-
|
|
26893
|
-
|
|
26894
|
-
|
|
26895
|
-
|
|
26896
|
-
|
|
26897
|
-
|
|
28445
|
+
bindings.push(
|
|
28446
|
+
markDirectSignalBinding(
|
|
28447
|
+
{
|
|
28448
|
+
id: bindings.length,
|
|
28449
|
+
kind: 'booleanAttr',
|
|
28450
|
+
name: attrName.toLowerCase(),
|
|
28451
|
+
expr,
|
|
28452
|
+
path,
|
|
28453
|
+
ns: hostNs,
|
|
28454
|
+
nameOrigin: attr.name,
|
|
28455
|
+
},
|
|
28456
|
+
ctx,
|
|
28457
|
+
inner,
|
|
28458
|
+
'binding',
|
|
28459
|
+
),
|
|
28460
|
+
);
|
|
26898
28461
|
} else if (
|
|
26899
28462
|
/^data-[a-z][a-z0-9_-]*$/.test(attrName) &&
|
|
26900
28463
|
isKnownStringExpression(inner, ctx.knownStringLocals)
|
|
@@ -26905,33 +28468,57 @@ function emitElementHtml(
|
|
|
26905
28468
|
// data attribute in HTML, SVG, and MathML, so destination-opaque component
|
|
26906
28469
|
// templates can retain this specialization. Production also admits unknown
|
|
26907
28470
|
// scalar data values through staticAttributeWriter below.
|
|
26908
|
-
bindings.push(
|
|
26909
|
-
|
|
26910
|
-
|
|
26911
|
-
|
|
26912
|
-
|
|
26913
|
-
|
|
26914
|
-
|
|
26915
|
-
|
|
26916
|
-
|
|
28471
|
+
bindings.push(
|
|
28472
|
+
markDirectSignalBinding(
|
|
28473
|
+
{
|
|
28474
|
+
id: bindings.length,
|
|
28475
|
+
kind: 'stringData',
|
|
28476
|
+
name: attrName,
|
|
28477
|
+
expr,
|
|
28478
|
+
path,
|
|
28479
|
+
ns: hostNs,
|
|
28480
|
+
nameOrigin: attr.name,
|
|
28481
|
+
},
|
|
28482
|
+
ctx,
|
|
28483
|
+
inner,
|
|
28484
|
+
'binding',
|
|
28485
|
+
),
|
|
28486
|
+
);
|
|
26917
28487
|
} else {
|
|
26918
28488
|
const writer = ctx.dev ? null : staticAttributeWriter(tag, attrName);
|
|
26919
|
-
bindings.push(
|
|
26920
|
-
|
|
26921
|
-
|
|
26922
|
-
|
|
26923
|
-
|
|
26924
|
-
|
|
26925
|
-
|
|
26926
|
-
|
|
26927
|
-
|
|
26928
|
-
|
|
26929
|
-
|
|
26930
|
-
|
|
26931
|
-
|
|
28489
|
+
bindings.push(
|
|
28490
|
+
markDirectSignalBinding(
|
|
28491
|
+
{
|
|
28492
|
+
id: bindings.length,
|
|
28493
|
+
kind: 'attr',
|
|
28494
|
+
attributeHelper: writer?.helper,
|
|
28495
|
+
name:
|
|
28496
|
+
ctx.dev && (rawAttrName === 'tabIndex' || rawAttrName === 'htmlFor')
|
|
28497
|
+
? rawAttrName
|
|
28498
|
+
: (writer?.name ?? attrName),
|
|
28499
|
+
expr,
|
|
28500
|
+
path,
|
|
28501
|
+
ns: hostNs,
|
|
28502
|
+
nameOrigin: attr.name,
|
|
28503
|
+
},
|
|
28504
|
+
ctx,
|
|
28505
|
+
inner,
|
|
28506
|
+
'binding',
|
|
28507
|
+
),
|
|
28508
|
+
);
|
|
26932
28509
|
}
|
|
26933
28510
|
}
|
|
26934
28511
|
if (resolveHostPropsAcrossSources) {
|
|
28512
|
+
const signalHostSources =
|
|
28513
|
+
firstSpreadIdx !== -1 ||
|
|
28514
|
+
hasDirectSignalStyle ||
|
|
28515
|
+
hostClientSources.some(
|
|
28516
|
+
(source) => !source.spread && canCarryDirectSignalHandle(source.binding.expr),
|
|
28517
|
+
);
|
|
28518
|
+
if (signalHostSources) {
|
|
28519
|
+
ctx.signalBindingsUsed = true;
|
|
28520
|
+
ctx.signalBindingsEager = true;
|
|
28521
|
+
}
|
|
26935
28522
|
hostCommitClientBinding = {
|
|
26936
28523
|
id: bindings.length,
|
|
26937
28524
|
kind: 'hostCommit',
|
|
@@ -26939,6 +28526,8 @@ function emitElementHtml(
|
|
|
26939
28526
|
origin: node,
|
|
26940
28527
|
sources: hostClientSources,
|
|
26941
28528
|
hasNestedChildren: hasNestedJsxChildren,
|
|
28529
|
+
signalHostSources,
|
|
28530
|
+
signalSite: signalHostSources ? hostSignalSite : null,
|
|
26942
28531
|
};
|
|
26943
28532
|
bindings.push(hostCommitClientBinding);
|
|
26944
28533
|
} else if (resolveDangerouslySetInnerHTMLAcrossSpreads) {
|
|
@@ -27014,13 +28603,16 @@ function emitElementHtml(
|
|
|
27014
28603
|
}
|
|
27015
28604
|
}
|
|
27016
28605
|
if (
|
|
28606
|
+
!VOID_ELEMENTS.has(tag) &&
|
|
27017
28607
|
!hasNestedJsxChildren &&
|
|
27018
28608
|
(hostCommitClientBinding !== null || directChildrenClientBinding !== null)
|
|
27019
28609
|
) {
|
|
27020
28610
|
compCalls.push({
|
|
27021
28611
|
id: ctx.nextHelperId++,
|
|
27022
28612
|
loc: devLoc(ctx, node),
|
|
28613
|
+
origin: node,
|
|
27023
28614
|
isChild: true,
|
|
28615
|
+
signalSite: directSignalSite(ctx, node, 'binding'),
|
|
27024
28616
|
hostPath: path,
|
|
27025
28617
|
onlyChildText: true,
|
|
27026
28618
|
potentialDangerouslySetInnerHTML,
|
|
@@ -27065,7 +28657,14 @@ function emitElementHtml(
|
|
|
27065
28657
|
if (hasNestedHoist) children = children.filter((n) => n.type !== 'HeadHoist');
|
|
27066
28658
|
}
|
|
27067
28659
|
// Special case: a single Text child (only-child text fast path).
|
|
27068
|
-
if (
|
|
28660
|
+
if (
|
|
28661
|
+
children.length === 1 &&
|
|
28662
|
+
children[0].type === 'Text' &&
|
|
28663
|
+
!children[0]._octaneBindingText &&
|
|
28664
|
+
!children[0]._octaneBindingOpaque &&
|
|
28665
|
+
!children[0]._octaneBindingValue &&
|
|
28666
|
+
!children[0]._octaneBindingSlot
|
|
28667
|
+
) {
|
|
27069
28668
|
const txtChild = children[0];
|
|
27070
28669
|
const staticLit = staticTextLiteral(txtChild.expression);
|
|
27071
28670
|
if (staticLit !== null) {
|
|
@@ -27089,15 +28688,23 @@ function emitElementHtml(
|
|
|
27089
28688
|
]
|
|
27090
28689
|
: null;
|
|
27091
28690
|
appendTemplatePart(html, escaped, 'text', origins);
|
|
27092
|
-
} else if (
|
|
28691
|
+
} else if (
|
|
28692
|
+
isDirectSignalHandleExpression(txtChild.expression) ||
|
|
28693
|
+
isKnownTextChildExpression(txtChild.expression, ctx.knownStringChildLocals)
|
|
28694
|
+
) {
|
|
28695
|
+
const binding = markDirectSignalBinding(
|
|
28696
|
+
{
|
|
28697
|
+
id: bindings.length,
|
|
28698
|
+
kind: 'textOnlyChild',
|
|
28699
|
+
expr: resolveStyleExpr(txtChild.expression, cssHash),
|
|
28700
|
+
path,
|
|
28701
|
+
},
|
|
28702
|
+
ctx,
|
|
28703
|
+
txtChild.expression,
|
|
28704
|
+
'binding',
|
|
28705
|
+
);
|
|
27093
28706
|
const seededText = tag !== 'template' && !tag.includes('-') && !directPropNames.has('is');
|
|
27094
|
-
bindings.push({
|
|
27095
|
-
id: bindings.length,
|
|
27096
|
-
kind: 'textOnlyChild',
|
|
27097
|
-
expr: resolveStyleExpr(txtChild.expression, cssHash),
|
|
27098
|
-
path,
|
|
27099
|
-
seededText,
|
|
27100
|
-
});
|
|
28707
|
+
bindings.push({ ...binding, seededText });
|
|
27101
28708
|
// A nonempty placeholder survives the HTML parser as one Text node. The
|
|
27102
28709
|
// mount writes its actual value before the cloned host is inserted, so
|
|
27103
28710
|
// even an empty value retains one stable text binding node. <template>
|
|
@@ -27239,8 +28846,23 @@ function emitElementHtml(
|
|
|
27239
28846
|
childIdx++;
|
|
27240
28847
|
continue;
|
|
27241
28848
|
}
|
|
27242
|
-
if (child.
|
|
27243
|
-
const
|
|
28849
|
+
if (child._octaneBindingValue || child._octaneBindingSlot) {
|
|
28850
|
+
const ch = makeChildCall(
|
|
28851
|
+
child.expression,
|
|
28852
|
+
ctx,
|
|
28853
|
+
componentName,
|
|
28854
|
+
inlinedSubs,
|
|
28855
|
+
cssHash,
|
|
28856
|
+
childNs,
|
|
28857
|
+
);
|
|
28858
|
+
ch.bindingMarker = bindingChildMarker(child);
|
|
28859
|
+
ch.hostPath = path;
|
|
28860
|
+
ch.anchorPath = [...path, childIdx];
|
|
28861
|
+
compCalls.push(ch);
|
|
28862
|
+
appendTemplatePart(html, '<!>', 'anchor');
|
|
28863
|
+
childIdx++;
|
|
28864
|
+
} else if (child.type === 'Text') {
|
|
28865
|
+
const staticLit = child._octaneBindingText ? null : staticTextLiteral(child.expression);
|
|
27244
28866
|
if (staticLit === '') {
|
|
27245
28867
|
// Renders nothing: bake nothing and consume no childIndex (an empty
|
|
27246
28868
|
// text produces NO node when the template HTML is parsed, so counting
|
|
@@ -27272,17 +28894,30 @@ function emitElementHtml(
|
|
|
27272
28894
|
appendTemplatePart(html, escaped, 'text', origins);
|
|
27273
28895
|
prevBakedText = true;
|
|
27274
28896
|
if (!prevBaked) childIdx++;
|
|
27275
|
-
} else if (
|
|
27276
|
-
|
|
27277
|
-
|
|
27278
|
-
|
|
27279
|
-
|
|
27280
|
-
|
|
27281
|
-
|
|
27282
|
-
|
|
27283
|
-
|
|
27284
|
-
|
|
27285
|
-
|
|
28897
|
+
} else if (
|
|
28898
|
+
isDirectSignalHandleExpression(child.expression) ||
|
|
28899
|
+
isKnownTextChildExpression(child.expression, ctx.knownStringChildLocals)
|
|
28900
|
+
) {
|
|
28901
|
+
bindings.push(
|
|
28902
|
+
markDirectSignalBinding(
|
|
28903
|
+
{
|
|
28904
|
+
id: bindings.length,
|
|
28905
|
+
kind: 'text',
|
|
28906
|
+
expr: resolveStyleExpr(child.expression, cssHash),
|
|
28907
|
+
bindingMarker: child._octaneBindingText
|
|
28908
|
+
? bindingMarker(child._octaneBindingText, 't')
|
|
28909
|
+
: undefined,
|
|
28910
|
+
path,
|
|
28911
|
+
childIndex: childIdx,
|
|
28912
|
+
// A text-producing neighbour → the server emits a `<!-- -->`
|
|
28913
|
+
// separator here; the walk must be hole-aware (see hasHoles).
|
|
28914
|
+
adjacentText: hasTextNeighbor(adjKinds, childI),
|
|
28915
|
+
},
|
|
28916
|
+
ctx,
|
|
28917
|
+
child.expression,
|
|
28918
|
+
'binding',
|
|
28919
|
+
),
|
|
28920
|
+
);
|
|
27286
28921
|
appendTemplatePart(html, '<!>', 'anchor'); // placeholder we'll replace at mount
|
|
27287
28922
|
childIdx++;
|
|
27288
28923
|
} else {
|
|
@@ -27461,24 +29096,36 @@ function emitElementHtml(
|
|
|
27461
29096
|
} else if (isConditionalJsx(expr)) {
|
|
27462
29097
|
// Lower `{cond ? A : B}` (where A or B is JSX) to an IfStatement so
|
|
27463
29098
|
// each branch renders real DOM via the existing ifBlock machinery.
|
|
27464
|
-
const asIf =
|
|
27465
|
-
...b.if(expr.test, wrapAsBlockStmt(expr.consequent), wrapAsBlockStmt(expr.alternate)),
|
|
27466
|
-
loc: expr.loc, // carry source position for dev hydration-mismatch LOC
|
|
27467
|
-
};
|
|
29099
|
+
const asIf = conditionalJsxStatement(expr);
|
|
27468
29100
|
const ic = makeIfCall(asIf, ctx, inlinedSubs, childNs, cssHash);
|
|
27469
29101
|
ic.hostPath = path;
|
|
27470
29102
|
ic.anchorPath = [...path, childIdx];
|
|
27471
29103
|
ifCalls.push(ic);
|
|
27472
29104
|
appendTemplatePart(html, '<!>', 'anchor');
|
|
27473
29105
|
childIdx++;
|
|
27474
|
-
} else if (
|
|
27475
|
-
|
|
27476
|
-
|
|
27477
|
-
|
|
27478
|
-
|
|
27479
|
-
|
|
27480
|
-
|
|
27481
|
-
|
|
29106
|
+
} else if (
|
|
29107
|
+
isDirectSignalHandleExpression(expr) ||
|
|
29108
|
+
isKnownTextChildExpression(expr, ctx.knownStringChildLocals)
|
|
29109
|
+
) {
|
|
29110
|
+
bindings.push(
|
|
29111
|
+
markDirectSignalBinding(
|
|
29112
|
+
{
|
|
29113
|
+
id: bindings.length,
|
|
29114
|
+
kind: 'text',
|
|
29115
|
+
expr: tsrxExprNode(
|
|
29116
|
+
resolveStyleExpr(expr, cssHash),
|
|
29117
|
+
ctx,
|
|
29118
|
+
componentName,
|
|
29119
|
+
inlinedSubs,
|
|
29120
|
+
),
|
|
29121
|
+
path,
|
|
29122
|
+
childIndex: childIdx,
|
|
29123
|
+
},
|
|
29124
|
+
ctx,
|
|
29125
|
+
expr,
|
|
29126
|
+
'binding',
|
|
29127
|
+
),
|
|
29128
|
+
);
|
|
27482
29129
|
appendTemplatePart(html, '<!>', 'anchor');
|
|
27483
29130
|
childIdx++;
|
|
27484
29131
|
} else {
|
|
@@ -27687,6 +29334,17 @@ function hoistBodyHelper(
|
|
|
27687
29334
|
id: b.id(helperName, idOrigin ?? fakeOrigin ?? undefined),
|
|
27688
29335
|
params: params || [],
|
|
27689
29336
|
body: bodyStmts,
|
|
29337
|
+
...(ctx.presentationHydration?.structural
|
|
29338
|
+
? {
|
|
29339
|
+
_octanePresentationHydration: {
|
|
29340
|
+
id: ctx.presentationHydration.id,
|
|
29341
|
+
supported:
|
|
29342
|
+
ctx.presentationHydration.supported ||
|
|
29343
|
+
ctx.presentationHydration.conditionalRest === true,
|
|
29344
|
+
structural: true,
|
|
29345
|
+
},
|
|
29346
|
+
}
|
|
29347
|
+
: {}),
|
|
27690
29348
|
},
|
|
27691
29349
|
fakeOrigin,
|
|
27692
29350
|
);
|
|
@@ -27831,6 +29489,7 @@ function makeIfCall(node, ctx, inlinedSubs, parentNs = 'html', cssHash = null) {
|
|
|
27831
29489
|
origin: node,
|
|
27832
29490
|
condExpr: node.test, // the fold replaces this with a `props.hN` member read
|
|
27833
29491
|
condTest: node.test, // raw test AST — the fold threads it as a `props.hN` hole
|
|
29492
|
+
bindingSite: node._octaneBindingSite,
|
|
27834
29493
|
envNames,
|
|
27835
29494
|
thenHelper: thenHelperName,
|
|
27836
29495
|
elseHelper: elseHelperName,
|
|
@@ -28079,6 +29738,7 @@ function makeChildCall(expr, ctx, componentName, inlinedSubs, cssHash, parentNs
|
|
|
28079
29738
|
loc: devLoc(ctx, expr),
|
|
28080
29739
|
origin: expr,
|
|
28081
29740
|
isChild: true,
|
|
29741
|
+
signalSite: directSignalSite(ctx, expr, 'binding'),
|
|
28082
29742
|
valueExpr: tsrxExprNode(
|
|
28083
29743
|
resolveStyleExpr(rewriteChildHoleValue(expr, ctx), cssHash),
|
|
28084
29744
|
ctx,
|
|
@@ -28288,6 +29948,7 @@ function makeCompCall(
|
|
|
28288
29948
|
cssHash = null,
|
|
28289
29949
|
) {
|
|
28290
29950
|
const id = ctx.nextHelperId++;
|
|
29951
|
+
const invocationSite = componentInvocationSite(ctx, node);
|
|
28291
29952
|
const compName = tagBindingName(node);
|
|
28292
29953
|
const staticFragmentRenderer = node.openingElement?.metadata?.staticFragmentRenderer;
|
|
28293
29954
|
const activityDescriptor = isActivityLongForm(node, ctx);
|
|
@@ -28427,23 +30088,31 @@ function makeCompCall(
|
|
|
28427
30088
|
ctx.hoistedHelpers.push(inheritOriginLoc(b.const(bodyIdentity, b.object([])), node));
|
|
28428
30089
|
childrenArgs.push(b.id(bodyIdentity));
|
|
28429
30090
|
}
|
|
28430
|
-
|
|
28431
|
-
|
|
28432
|
-
|
|
30091
|
+
let childrenValue = b.call('_$markChildrenBlock', ...childrenArgs);
|
|
30092
|
+
if (ctx.presentationHydration?.structural && node._octaneBindingSite)
|
|
30093
|
+
childrenValue = b.call(
|
|
30094
|
+
requireRuntimeForContext(ctx, 'markBindingChildren'),
|
|
30095
|
+
childrenValue,
|
|
30096
|
+
b.literal(node._octaneBindingSite.id + ';' + node._octaneBindingSite.site),
|
|
30097
|
+
);
|
|
30098
|
+
propNodes.push(b.prop('init', b.literal('children'), childrenValue));
|
|
28433
30099
|
}
|
|
28434
30100
|
}
|
|
28435
30101
|
|
|
28436
30102
|
// The props object as a node; the call-site emit embeds it directly.
|
|
28437
30103
|
const propsExpr = staticFragmentRenderer?.props ?? inheritOriginLoc(b.object(propNodes), node);
|
|
28438
30104
|
if (descriptorConfig) {
|
|
28439
|
-
ctx.runtimeNeeded.add('
|
|
30105
|
+
ctx.runtimeNeeded.add('createElementFromConfig');
|
|
28440
30106
|
return {
|
|
28441
30107
|
id,
|
|
28442
30108
|
loc: devLoc(ctx, node),
|
|
28443
30109
|
origin: node,
|
|
28444
30110
|
isChild: true,
|
|
28445
30111
|
anchorlessAppendSafe: true,
|
|
28446
|
-
valueExpr: inheritOriginLoc(
|
|
30112
|
+
valueExpr: inheritOriginLoc(
|
|
30113
|
+
b.call('_$createElementFromConfig', b.literal(invocationSite), compNode, propsExpr),
|
|
30114
|
+
node,
|
|
30115
|
+
),
|
|
28447
30116
|
};
|
|
28448
30117
|
}
|
|
28449
30118
|
|
|
@@ -28619,11 +30288,13 @@ function makeCompCall(
|
|
|
28619
30288
|
|
|
28620
30289
|
return {
|
|
28621
30290
|
id,
|
|
30291
|
+
invocationSite,
|
|
28622
30292
|
compNode,
|
|
28623
30293
|
propsExpr,
|
|
28624
30294
|
hostPath: null,
|
|
28625
30295
|
keyExpr,
|
|
28626
|
-
|
|
30296
|
+
bindingSite: node._octaneBindingSite,
|
|
30297
|
+
liteEligible: node._octaneBindingSite ? false : liteEligible,
|
|
28627
30298
|
anchorlessAppendSafe,
|
|
28628
30299
|
autoMemoDeps,
|
|
28629
30300
|
autoMemoDepNodes,
|
|
@@ -28632,8 +30303,8 @@ function makeCompCall(
|
|
|
28632
30303
|
autoMemoLite,
|
|
28633
30304
|
autoMemoPublicationWitnesses,
|
|
28634
30305
|
voidComponent,
|
|
28635
|
-
singleRoot,
|
|
28636
|
-
maybeSingleRoot,
|
|
30306
|
+
singleRoot: node._octaneBindingSite ? false : singleRoot,
|
|
30307
|
+
maybeSingleRoot: node._octaneBindingSite ? false : maybeSingleRoot,
|
|
28637
30308
|
loc: devLoc(ctx, node),
|
|
28638
30309
|
origin: node,
|
|
28639
30310
|
};
|
|
@@ -29411,6 +31082,10 @@ function makeForCall(node, ctx, inlinedSubs, parentNs = 'html', cssHash = null)
|
|
|
29411
31082
|
}
|
|
29412
31083
|
}
|
|
29413
31084
|
|
|
31085
|
+
if (node._octaneBindingSite) {
|
|
31086
|
+
singleRoot = false;
|
|
31087
|
+
singleRootExpr = null;
|
|
31088
|
+
}
|
|
29414
31089
|
const ssrMarkerless = isSsrMarkerlessForItem(node);
|
|
29415
31090
|
const hostRootTag = subStmts[0]?.id?.name ?? subStmts[0]?.openingElement?.name?.name;
|
|
29416
31091
|
const hostMountSafe =
|