octane 0.2.11 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/behavior-root.d.ts +13 -3
- package/dist/behavior-root.js +28 -5
- package/dist/cjs/behavior-root.cjs +32 -6
- package/dist/cjs/class-names.cjs +49 -0
- package/dist/cjs/constants.cjs +9 -0
- package/dist/cjs/context-epoch.cjs +38 -0
- package/dist/cjs/css.cjs +6 -27
- package/dist/cjs/data-encoding.cjs +160 -0
- package/dist/cjs/dom-binding-handoff.cjs +93 -0
- package/dist/cjs/dom-binding-protocol.cjs +115 -0
- package/dist/cjs/dom-binding-styles.cjs +292 -0
- package/dist/cjs/dom-bindings.cjs +700 -0
- package/dist/cjs/dom-focused-move.cjs +66 -0
- package/dist/cjs/dom-stage.cjs +896 -0
- package/dist/cjs/dom-tables.cjs +4 -94
- package/dist/cjs/error-codes.client.generated.cjs +33 -0
- package/dist/cjs/error-codes.server.generated.cjs +24 -0
- package/dist/cjs/hydration/control-capture.cjs +244 -0
- package/dist/cjs/hydration/event-capture.cjs +125 -43
- package/dist/cjs/hydration/interaction-config.cjs +50 -2
- package/dist/cjs/hydration-markers.cjs +37 -0
- package/dist/cjs/independent-hydration-protocol.cjs +106 -0
- package/dist/cjs/index.cjs +9 -0
- package/dist/cjs/runtime.cjs +6397 -1683
- package/dist/cjs/runtime.server.cjs +1465 -213
- package/dist/cjs/server/early-signals.cjs +45 -0
- package/dist/cjs/server/index.cjs +47 -0
- package/dist/cjs/server/rpc-batch.cjs +149 -0
- package/dist/cjs/server/rpc-stream.cjs +146 -0
- package/dist/cjs/server/rpc.cjs +34 -15
- package/dist/cjs/server/signal-query-observation.cjs +158 -0
- package/dist/cjs/server/streamed-region.cjs +52 -0
- package/dist/cjs/server/streamed-signals.cjs +512 -0
- package/dist/cjs/server-call.cjs +103 -0
- package/dist/cjs/server-rpc-batch-client.cjs +141 -0
- package/dist/cjs/server-rpc-client.cjs +94 -27
- package/dist/cjs/server-rpc-protocol.cjs +141 -0
- package/dist/cjs/server-rpc-stream-client.cjs +118 -0
- package/dist/cjs/signals/actions.cjs +474 -0
- package/dist/cjs/signals/client.cjs +4 -1
- package/dist/cjs/signals/computations.cjs +460 -0
- package/dist/cjs/signals/control-binding.cjs +368 -0
- package/dist/cjs/signals/derived-facade.cjs +50 -0
- package/dist/cjs/signals/document-owner.cjs +71 -0
- package/dist/cjs/signals/early-values.cjs +155 -0
- package/dist/cjs/signals/encoding.cjs +6 -95
- package/dist/cjs/signals/engine.cjs +276 -12
- package/dist/cjs/signals/errors.cjs +18 -0
- package/dist/cjs/signals/facade.cjs +477 -0
- package/dist/cjs/signals/graph.cjs +166 -20
- package/dist/cjs/signals/handle-protocol.cjs +36 -0
- package/dist/cjs/signals/index.cjs +81 -2
- package/dist/cjs/signals/native-read-client.cjs +10 -0
- package/dist/cjs/signals/owner-context.cjs +112 -0
- package/dist/cjs/signals/query-attempt-observer.cjs +68 -0
- package/dist/cjs/signals/query-facade.cjs +72 -0
- package/dist/cjs/signals/read-protocol.cjs +54 -0
- package/dist/cjs/signals/requests.cjs +287 -14
- package/dist/cjs/signals/scalar-computations.cjs +76 -0
- package/dist/cjs/signals/server.cjs +4 -1
- package/dist/cjs/signals/transition-candidate.cjs +567 -0
- package/dist/cjs/signals/transition-state.cjs +106 -0
- package/dist/cjs/signals/types.cjs +50 -0
- package/dist/cjs/stream-protocol.cjs +3 -0
- package/dist/cjs/streamed-signals-protocol.cjs +159 -0
- package/dist/cjs/style-values.cjs +122 -0
- package/dist/cjs/trusted-html.cjs +30 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/class-names.d.ts +5 -0
- package/dist/class-names.js +25 -0
- package/dist/compiler/bundler.js +51 -22
- package/dist/compiler/compile.js +2534 -581
- package/dist/compiler/dom-binding-program.js +935 -0
- package/dist/compiler/dom-binding-request.js +57 -0
- package/dist/compiler/dom-bindings.js +2245 -0
- package/dist/compiler/hook-deps.js +759 -10
- package/dist/compiler/hydrate-boundaries.js +358 -15
- package/dist/compiler/index.d.ts +39 -2
- package/dist/compiler/index.js +1 -0
- package/dist/compiler/native-attribute-reads.js +127 -0
- package/dist/compiler/native-change-diagnostics.js +32 -38
- package/dist/compiler/native-read-diagnostics.js +69 -8
- package/dist/compiler/native-read-types.js +80 -34
- package/dist/compiler/parser.node.js +1 -1
- package/dist/compiler/renderer-boundaries.js +40 -0
- package/dist/compiler/server-context.js +118 -0
- package/dist/compiler/signal-attempt-reads.js +479 -0
- package/dist/compiler/signal-declarations.js +557 -0
- package/dist/compiler/signal-start-reads.js +413 -0
- package/dist/compiler/slot-hooks.d.ts +3 -1
- package/dist/compiler/slot-hooks.js +168 -34
- package/dist/compiler/strong-auto-memo.js +90 -0
- package/dist/compiler/strong-html.js +143 -0
- package/dist/compiler/strong-mode.js +764 -66
- package/dist/compiler/strong-template-policy.js +307 -0
- package/dist/compiler/template-ir.js +200 -0
- package/dist/compiler/typescript.d.ts +2 -0
- package/dist/compiler/typescript.js +2 -0
- package/dist/compiler/vite.d.ts +3 -1
- package/dist/compiler/vite.js +122 -4
- package/dist/compiler/volar.LICENSES.txt +1 -1
- package/dist/compiler/volar.d.ts +6 -1
- package/dist/compiler/volar.js +750 -1433
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +10 -0
- package/dist/context-epoch.d.ts +2 -0
- package/dist/context-epoch.js +13 -0
- package/dist/css.d.ts +2 -5
- package/dist/css.js +2 -23
- package/dist/data-encoding.d.ts +9 -0
- package/dist/data-encoding.js +134 -0
- package/dist/dom-binding-classes.d.ts +15 -0
- package/dist/dom-binding-classes.js +129 -0
- package/dist/dom-binding-controls.d.ts +2 -0
- package/dist/dom-binding-controls.js +6 -0
- package/dist/dom-binding-handoff.d.ts +55 -0
- package/dist/dom-binding-handoff.js +62 -0
- package/dist/dom-binding-program.d.ts +300 -0
- package/dist/dom-binding-program.js +1625 -0
- package/dist/dom-binding-projections.d.ts +15 -0
- package/dist/dom-binding-projections.js +187 -0
- package/dist/dom-binding-protocol.d.ts +13 -0
- package/dist/dom-binding-protocol.js +88 -0
- package/dist/dom-binding-signals.d.ts +21 -0
- package/dist/dom-binding-signals.js +109 -0
- package/dist/dom-binding-styles.d.ts +17 -0
- package/dist/dom-binding-styles.js +272 -0
- package/dist/dom-bindings.d.ts +89 -0
- package/dist/dom-bindings.js +670 -0
- package/dist/dom-focused-move.d.ts +6 -0
- package/dist/dom-focused-move.js +42 -0
- package/dist/dom-stage.d.ts +52 -0
- package/dist/dom-stage.js +872 -0
- package/dist/dom-tables.d.ts +1 -3
- package/dist/dom-tables.js +1 -91
- package/dist/error-codes.client.generated.d.ts +9 -0
- package/dist/error-codes.client.generated.js +33 -0
- package/dist/error-codes.server.generated.d.ts +6 -0
- package/dist/error-codes.server.generated.js +24 -0
- package/dist/hydration/control-capture.d.ts +62 -0
- package/dist/hydration/control-capture.js +223 -0
- package/dist/hydration/document-lifecycle.d.ts +23 -0
- package/dist/hydration/document-lifecycle.js +80 -0
- package/dist/hydration/event-capture.d.ts +20 -1
- package/dist/hydration/event-capture.js +128 -42
- package/dist/hydration/independent-island.d.ts +45 -0
- package/dist/hydration/independent-island.js +233 -0
- package/dist/hydration/index.d.ts +8 -1
- package/dist/hydration/index.js +55 -1
- package/dist/hydration/interaction-config.d.ts +5 -0
- package/dist/hydration/interaction-config.js +44 -1
- package/dist/hydration/stream-delivery.d.ts +32 -0
- package/dist/hydration/stream-delivery.js +333 -0
- package/dist/hydration/stream-receiver.d.ts +26 -0
- package/dist/hydration/stream-receiver.js +204 -0
- package/dist/hydration/stream-result-receiver.d.ts +70 -0
- package/dist/hydration/stream-result-receiver.js +246 -0
- package/dist/hydration/streamed-signals.d.ts +44 -0
- package/dist/hydration/streamed-signals.js +186 -0
- package/dist/hydration/types.d.ts +6 -0
- package/dist/hydration-markers.d.ts +7 -0
- package/dist/hydration-markers.js +10 -0
- package/dist/independent-hydration-protocol.d.ts +31 -0
- package/dist/independent-hydration-protocol.js +80 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +6 -0
- package/dist/internal/signal-read.d.ts +1 -0
- package/dist/internal/signal-read.js +4 -0
- package/dist/jsx-runtime-strong.d.ts +33 -0
- package/dist/jsx-runtime.d.ts +58 -2
- package/dist/public-types.d.ts +15 -1
- package/dist/runtime.d.ts +297 -23
- package/dist/runtime.js +6405 -1679
- package/dist/runtime.server.d.ts +110 -27
- package/dist/runtime.server.js +1458 -213
- package/dist/server/early-signals.d.ts +14 -0
- package/dist/server/early-signals.js +34 -0
- package/dist/server/index.d.ts +11 -2
- package/dist/server/index.js +50 -1
- package/dist/server/rpc-batch.d.ts +17 -0
- package/dist/server/rpc-batch.js +129 -0
- package/dist/server/rpc-stream.d.ts +9 -0
- package/dist/server/rpc-stream.js +125 -0
- package/dist/server/rpc.d.ts +5 -1
- package/dist/server/rpc.js +37 -13
- package/dist/server/signal-query-observation.d.ts +3 -0
- package/dist/server/signal-query-observation.js +134 -0
- package/dist/server/streamed-region.d.ts +15 -0
- package/dist/server/streamed-region.js +30 -0
- package/dist/server/streamed-signals.d.ts +45 -0
- package/dist/server/streamed-signals.js +488 -0
- package/dist/server-call.d.ts +41 -0
- package/dist/server-call.js +75 -0
- package/dist/server-rpc-batch-client.d.ts +7 -0
- package/dist/server-rpc-batch-client.js +119 -0
- package/dist/server-rpc-client.d.ts +3 -1
- package/dist/server-rpc-client.js +99 -27
- package/dist/server-rpc-protocol.d.ts +47 -0
- package/dist/server-rpc-protocol.js +109 -0
- package/dist/server-rpc-stream-client.d.ts +5 -0
- package/dist/server-rpc-stream-client.js +98 -0
- package/dist/signals/actions.d.ts +12 -0
- package/dist/signals/actions.js +466 -0
- package/dist/signals/client.d.ts +2 -0
- package/dist/signals/client.js +1 -0
- package/dist/signals/computations.d.ts +37 -0
- package/dist/signals/computations.js +455 -0
- package/dist/signals/control-binding.d.ts +31 -0
- package/dist/signals/control-binding.js +353 -0
- package/dist/signals/derived-facade.d.ts +3 -0
- package/dist/signals/derived-facade.js +25 -0
- package/dist/signals/document-owner.d.ts +10 -0
- package/dist/signals/document-owner.js +43 -0
- package/dist/signals/early-values.d.ts +24 -0
- package/dist/signals/early-values.js +124 -0
- package/dist/signals/encoding.d.ts +1 -3
- package/dist/signals/encoding.js +3 -93
- package/dist/signals/engine.d.ts +53 -4
- package/dist/signals/engine.js +275 -16
- package/dist/signals/errors.d.ts +7 -0
- package/dist/signals/errors.js +16 -0
- package/dist/signals/facade.d.ts +74 -0
- package/dist/signals/facade.js +452 -0
- package/dist/signals/graph.d.ts +52 -6
- package/dist/signals/graph.js +174 -19
- package/dist/signals/handle-protocol.d.ts +4 -0
- package/dist/signals/handle-protocol.js +11 -0
- package/dist/signals/index.d.ts +12 -3
- package/dist/signals/index.js +71 -2
- package/dist/signals/native-read-client.d.ts +3 -0
- package/dist/signals/native-read-client.js +10 -0
- package/dist/signals/owner-context.d.ts +22 -0
- package/dist/signals/owner-context.js +77 -0
- package/dist/signals/query-attempt-observer.d.ts +49 -0
- package/dist/signals/query-attempt-observer.js +41 -0
- package/dist/signals/query-facade.d.ts +3 -0
- package/dist/signals/query-facade.js +49 -0
- package/dist/signals/read-protocol.d.ts +37 -2
- package/dist/signals/read-protocol.js +44 -0
- package/dist/signals/requests.d.ts +38 -5
- package/dist/signals/requests.js +298 -15
- package/dist/signals/scalar-computations.d.ts +2 -0
- package/dist/signals/scalar-computations.js +60 -0
- package/dist/signals/server.d.ts +2 -0
- package/dist/signals/server.js +1 -0
- package/dist/signals/transition-candidate.d.ts +71 -0
- package/dist/signals/transition-candidate.js +557 -0
- package/dist/signals/transition-state.d.ts +51 -0
- package/dist/signals/transition-state.js +70 -0
- package/dist/signals/types.d.ts +95 -5
- package/dist/signals/types.js +20 -0
- package/dist/stream-protocol.d.ts +0 -5
- package/dist/stream-protocol.js +3 -0
- package/dist/streamed-signals-protocol.d.ts +60 -0
- package/dist/streamed-signals-protocol.js +130 -0
- package/dist/style-values.d.ts +3 -0
- package/dist/style-values.js +96 -0
- package/dist/trusted-html.d.ts +17 -0
- package/dist/trusted-html.js +6 -0
- package/dist/universal-core.d.ts +6 -0
- package/dist/universal-core.js +214 -202
- package/dist/version.js +1 -1
- package/package.json +48 -5
package/dist/compiler/compile.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Architecture:
|
|
6
6
|
* 1. Parse TSRX through the environment-selected @tsrx/core-compatible
|
|
7
|
-
* parser (native oxc
|
|
7
|
+
* parser (native @tsrx/oxc in Node, pure JavaScript elsewhere), then run
|
|
8
8
|
* @tsrx/core's target-neutral semantic analysis on the authored module.
|
|
9
9
|
* 2. For each top-level node:
|
|
10
10
|
* - Component (`@{ … }` body or a return-JSX function) → compile to a
|
|
@@ -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,
|
|
@@ -82,6 +89,7 @@ import { assertNoLiveClientOnlyImports } from './client-only-server.js';
|
|
|
82
89
|
import { nsForChildren, nsForSelf } from './jsx-namespace.js';
|
|
83
90
|
import { analyzeNativeChangeDiagnostics } from './native-change-diagnostics.js';
|
|
84
91
|
import { assertStrongMode } from './strong-mode.js';
|
|
92
|
+
import { applyStrongAutomaticMemo } from './strong-auto-memo.js';
|
|
85
93
|
import {
|
|
86
94
|
assertNativeReadDiagnostics,
|
|
87
95
|
assertNativeReadOptions,
|
|
@@ -92,7 +100,24 @@ import {
|
|
|
92
100
|
wrapNativeReadScope,
|
|
93
101
|
wrapNativeWarmScope,
|
|
94
102
|
} from './native-read-codegen.js';
|
|
95
|
-
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';
|
|
96
121
|
import { collectProvenContextBindings, isProvenContextUse } from './context-use.js';
|
|
97
122
|
import { applyCssModuleConstants } from './css-module-constants.js';
|
|
98
123
|
import { assertUniversalRuntimeTarget, normalizeUniversalRuntime } from './universal-runtime.js';
|
|
@@ -438,7 +463,7 @@ function attrBindingHelper(bind) {
|
|
|
438
463
|
case 'styleProperty':
|
|
439
464
|
return 'setStyleProperty';
|
|
440
465
|
case 'styleProperties':
|
|
441
|
-
return 'setStyleProperties';
|
|
466
|
+
return bind.spread ? 'setStyle' : 'setStyleProperties';
|
|
442
467
|
// SVG/MathML `className` is read-only — setClassAttr sets the attribute and
|
|
443
468
|
// clsx-composes (setClassName composes on HTML).
|
|
444
469
|
case 'class':
|
|
@@ -506,6 +531,135 @@ function attrBindingUpdateHelper(bind, inlineBindingGuards = false) {
|
|
|
506
531
|
}
|
|
507
532
|
}
|
|
508
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
|
+
|
|
509
663
|
/**
|
|
510
664
|
* An ALIAS: an authored range that emits nothing of its own but means the same
|
|
511
665
|
* thing as one that does. A component's closing tag is the case — `</Card>` is
|
|
@@ -877,8 +1031,8 @@ function hasPotentialDangerouslySetInnerHTML(node) {
|
|
|
877
1031
|
const attrs = node.attributes || node.openingElement?.attributes || [];
|
|
878
1032
|
return attrs.some(
|
|
879
1033
|
(attr) =>
|
|
880
|
-
attr.type === 'SpreadAttribute' ||
|
|
881
|
-
|
|
1034
|
+
((attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') &&
|
|
1035
|
+
!attr._octaneKnownAttributeSpread) ||
|
|
882
1036
|
((attr.type === 'Attribute' || attr.type === 'JSXAttribute') &&
|
|
883
1037
|
jsxAttrRawName(attr) === 'dangerouslySetInnerHTML'),
|
|
884
1038
|
);
|
|
@@ -888,6 +1042,7 @@ function hasDefinitelyPresentDirectDangerouslySetInnerHTML(node) {
|
|
|
888
1042
|
const attrs = node.attributes || node.openingElement?.attributes || [];
|
|
889
1043
|
let state = false;
|
|
890
1044
|
for (const attr of attrs) {
|
|
1045
|
+
if (attr._octaneKnownAttributeSpread) continue;
|
|
891
1046
|
if (attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') {
|
|
892
1047
|
// A later spread may overwrite or introduce the prop, so the effective
|
|
893
1048
|
// writer is no longer statically known. A following direct writer can
|
|
@@ -914,6 +1069,7 @@ function hasDefinitelyPresentDirectChildrenProp(node) {
|
|
|
914
1069
|
const attrs = node.attributes || node.openingElement?.attributes || [];
|
|
915
1070
|
let state = false;
|
|
916
1071
|
for (const attr of attrs) {
|
|
1072
|
+
if (attr._octaneKnownAttributeSpread) continue;
|
|
917
1073
|
if (attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') {
|
|
918
1074
|
state = null;
|
|
919
1075
|
continue;
|
|
@@ -1190,6 +1346,10 @@ const HOOK_MEMO_RUNTIME_HELPERS = new Set([
|
|
|
1190
1346
|
'memoPublishAlways',
|
|
1191
1347
|
]);
|
|
1192
1348
|
const NATIVE_READ_RUNTIME_HELPERS = new Set([
|
|
1349
|
+
'nativeStyleBinding',
|
|
1350
|
+
'nativeProjectionBinding',
|
|
1351
|
+
'readNativeDomStyle',
|
|
1352
|
+
'readNativeDomProps',
|
|
1193
1353
|
'enableNativeReadCollection',
|
|
1194
1354
|
'beginNativeReadScope',
|
|
1195
1355
|
'endNativeReadScope',
|
|
@@ -1209,6 +1369,29 @@ const NATIVE_READ_RUNTIME_HELPERS = new Set([
|
|
|
1209
1369
|
'nativeCreateScopedElement',
|
|
1210
1370
|
]);
|
|
1211
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',
|
|
1393
|
+
'canSplitStyleProperties',
|
|
1394
|
+
'styleObjectPrototype',
|
|
1212
1395
|
'replaceRef',
|
|
1213
1396
|
'queueOwnRefDetach',
|
|
1214
1397
|
'setPlainAttribute',
|
|
@@ -1229,6 +1412,18 @@ const INTERNAL_CLIENT_RUNTIME_HELPERS = new Set([
|
|
|
1229
1412
|
...NATIVE_READ_RUNTIME_HELPERS,
|
|
1230
1413
|
]);
|
|
1231
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',
|
|
1232
1427
|
'ssrSpreadContent',
|
|
1233
1428
|
...NATIVE_READ_RUNTIME_HELPERS,
|
|
1234
1429
|
]);
|
|
@@ -1611,6 +1806,7 @@ function lowerImportedErrorBoundaries(ast) {
|
|
|
1611
1806
|
function inheritSoleCompRoot(bodyNodes, ctx) {
|
|
1612
1807
|
if (bodyNodes.length !== 1) return false;
|
|
1613
1808
|
const n = bodyNodes[0];
|
|
1809
|
+
if (n._octaneBindingSite !== undefined) return false;
|
|
1614
1810
|
if (n.type !== 'Element' || !isComponentTag(n)) return false;
|
|
1615
1811
|
// Returned-JSX extraction can merge an authored key/spread config into one
|
|
1616
1812
|
// spread hole. Its descriptor still needs its own keyed reconciliation range.
|
|
@@ -2208,15 +2404,23 @@ function staticObjectToCssString(obj) {
|
|
|
2208
2404
|
// A literal prefix can live in the template when all following properties have
|
|
2209
2405
|
// fixed, unique names. The values still evaluate together at the authored
|
|
2210
2406
|
// style attribute, before any DOM writes. Fixed literal values
|
|
2211
|
-
// after the first dynamic property stay in the ordered suffix.
|
|
2212
|
-
//
|
|
2407
|
+
// after the first dynamic property stay in the ordered suffix. Leading spreads
|
|
2408
|
+
// retain an object snapshot; only their fixed trailing properties are split.
|
|
2409
|
+
// Computed names and accessors retain ordinary object evaluation and diffing.
|
|
2213
2410
|
function mixedStaticStyle(obj) {
|
|
2214
2411
|
if (obj?.type !== 'ObjectExpression' || obj.properties.length === 0) return null;
|
|
2412
|
+
let spreadCount = 0;
|
|
2413
|
+
while (obj.properties[spreadCount]?.type === 'SpreadElement') spreadCount++;
|
|
2414
|
+
if (spreadCount === obj.properties.length) return null;
|
|
2415
|
+
const spread = spreadCount
|
|
2416
|
+
? inheritOriginLoc(b.object(obj.properties.slice(0, spreadCount)), obj)
|
|
2417
|
+
: null;
|
|
2418
|
+
const properties = spreadCount ? obj.properties.slice(spreadCount) : obj.properties;
|
|
2215
2419
|
const seen = new Map();
|
|
2216
2420
|
let duplicates = false;
|
|
2217
2421
|
const prefix = [];
|
|
2218
2422
|
const dynamics = [];
|
|
2219
|
-
for (const property of
|
|
2423
|
+
for (const property of properties) {
|
|
2220
2424
|
if (
|
|
2221
2425
|
(property.type !== 'Property' && property.type !== 'ObjectProperty') ||
|
|
2222
2426
|
property.computed ||
|
|
@@ -2234,6 +2438,9 @@ function mixedStaticStyle(obj) {
|
|
|
2234
2438
|
if (
|
|
2235
2439
|
name === '__proto__' ||
|
|
2236
2440
|
name === 'dangerouslySetInnerHTML' ||
|
|
2441
|
+
// Integer keys enumerate before the spread's string keys, regardless
|
|
2442
|
+
// of their authored position. Keep their coercion order on the object path.
|
|
2443
|
+
(spread !== null && String(Number(name) >>> 0) === name && name !== '4294967295') ||
|
|
2237
2444
|
(seen.has(normalized) && seen.get(normalized) !== name)
|
|
2238
2445
|
) {
|
|
2239
2446
|
return null;
|
|
@@ -2255,6 +2462,7 @@ function mixedStaticStyle(obj) {
|
|
|
2255
2462
|
return null;
|
|
2256
2463
|
}
|
|
2257
2464
|
if (
|
|
2465
|
+
spread === null &&
|
|
2258
2466
|
value?.type === 'Literal' &&
|
|
2259
2467
|
dynamics.length === 0 &&
|
|
2260
2468
|
(typeof value.value === 'string' || typeof value.value === 'number') &&
|
|
@@ -2271,7 +2479,8 @@ function mixedStaticStyle(obj) {
|
|
|
2271
2479
|
if (duplicates) {
|
|
2272
2480
|
return {
|
|
2273
2481
|
css: '',
|
|
2274
|
-
|
|
2482
|
+
spread,
|
|
2483
|
+
dynamics: properties.map((property) => ({
|
|
2275
2484
|
name: property.key.type === 'Identifier' ? property.key.name : property.key.value,
|
|
2276
2485
|
key: property.key,
|
|
2277
2486
|
value: property.value,
|
|
@@ -2280,7 +2489,7 @@ function mixedStaticStyle(obj) {
|
|
|
2280
2489
|
}
|
|
2281
2490
|
if (dynamics.length === 0) return null;
|
|
2282
2491
|
const css = staticObjectToCssString({ properties: prefix });
|
|
2283
|
-
return prefix.length !== 0 && css === '' ? null : { css, dynamics };
|
|
2492
|
+
return prefix.length !== 0 && css === '' ? null : { css, dynamics, spread };
|
|
2284
2493
|
}
|
|
2285
2494
|
|
|
2286
2495
|
// ===========================================================================
|
|
@@ -4467,6 +4676,7 @@ function containsAutoMemoContextRead(root, ctx) {
|
|
|
4467
4676
|
}
|
|
4468
4677
|
if (seen.has(node)) return;
|
|
4469
4678
|
seen.add(node);
|
|
4679
|
+
|
|
4470
4680
|
if (
|
|
4471
4681
|
node.type === 'ArrowFunctionExpression' ||
|
|
4472
4682
|
node.type === 'FunctionExpression' ||
|
|
@@ -6365,6 +6575,26 @@ function containsAutoMemoUnsafeStructure(stmts, ctx = null) {
|
|
|
6365
6575
|
if (containsDeferredRefRead(n)) found = true;
|
|
6366
6576
|
return;
|
|
6367
6577
|
}
|
|
6578
|
+
// Native styles perform implicit reads, including styles carried by a host
|
|
6579
|
+
// prop spread. Equal props cannot prove the subtree complete during a
|
|
6580
|
+
// boundary retry: these reads are outside the parent call's memo witness.
|
|
6581
|
+
if (
|
|
6582
|
+
ctx?.nativeReads &&
|
|
6583
|
+
n.type === 'JSXOpeningElement' &&
|
|
6584
|
+
n.name?.type === 'JSXIdentifier' &&
|
|
6585
|
+
/^[a-z]/.test(n.name.name) &&
|
|
6586
|
+
n.attributes.some((attribute) => {
|
|
6587
|
+
if (attribute.type === 'JSXSpreadAttribute' || attribute.type === 'SpreadAttribute')
|
|
6588
|
+
return true;
|
|
6589
|
+
if (attribute.name?.name !== 'style' || attribute.value?.type !== 'JSXExpressionContainer')
|
|
6590
|
+
return false;
|
|
6591
|
+
const value = unwrapTsExpr(attribute.value.expression);
|
|
6592
|
+
return value.type !== 'ObjectExpression' || !objectExprIsStaticLiteral(value);
|
|
6593
|
+
})
|
|
6594
|
+
) {
|
|
6595
|
+
found = true;
|
|
6596
|
+
return;
|
|
6597
|
+
}
|
|
6368
6598
|
if (
|
|
6369
6599
|
t === 'AssignmentExpression' ||
|
|
6370
6600
|
t === 'AssignmentPattern' ||
|
|
@@ -6795,6 +7025,19 @@ function wrapAsBlockStmt(node) {
|
|
|
6795
7025
|
return b.block([node]);
|
|
6796
7026
|
}
|
|
6797
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
|
+
|
|
6798
7041
|
/** `xs.map(x => <li/>)` — detect so we can throw a useful "use for-of" error. */
|
|
6799
7042
|
function isJsxReturningMapCall(node) {
|
|
6800
7043
|
if (!node || node.type !== 'CallExpression') return false;
|
|
@@ -7298,6 +7541,7 @@ function anchorlessRootShape(node) {
|
|
|
7298
7541
|
* the hydrator can use it as the keyed item boundary without an item pair.
|
|
7299
7542
|
*/
|
|
7300
7543
|
function isSsrMarkerlessForItem(node) {
|
|
7544
|
+
if (node?._octaneBindingSite !== undefined) return false;
|
|
7301
7545
|
const body = node?.body?.body || [];
|
|
7302
7546
|
const jsxChildren = body.filter((s) => isJsxNode(s));
|
|
7303
7547
|
return jsxChildren.length === 1 && isPlainHostRoot(jsxChildren[0]);
|
|
@@ -7878,21 +8122,6 @@ function identifierName(node) {
|
|
|
7878
8122
|
return null;
|
|
7879
8123
|
}
|
|
7880
8124
|
|
|
7881
|
-
function unwrapServerFunctionInitializer(node) {
|
|
7882
|
-
let current = node;
|
|
7883
|
-
while (
|
|
7884
|
-
current &&
|
|
7885
|
-
(current.type === 'TSAsExpression' ||
|
|
7886
|
-
current.type === 'TSSatisfiesExpression' ||
|
|
7887
|
-
current.type === 'TSNonNullExpression' ||
|
|
7888
|
-
current.type === 'TypeCastExpression' ||
|
|
7889
|
-
current.type === 'ParenthesizedExpression')
|
|
7890
|
-
) {
|
|
7891
|
-
current = current.expression;
|
|
7892
|
-
}
|
|
7893
|
-
return current;
|
|
7894
|
-
}
|
|
7895
|
-
|
|
7896
8125
|
function collectServerFunctionBindings(statements) {
|
|
7897
8126
|
const functions = new Set();
|
|
7898
8127
|
const aliases = new Map();
|
|
@@ -8055,6 +8284,8 @@ function analyzeServerModule(ast, filename) {
|
|
|
8055
8284
|
const statements = declaration.body?.body || [];
|
|
8056
8285
|
validateServerModuleIsolation(ast, declaration, filename);
|
|
8057
8286
|
const functionBindings = collectServerFunctionBindings(statements);
|
|
8287
|
+
const functionNodes = collectServerFunctionNodes(statements);
|
|
8288
|
+
const contextImports = serverContextTypeImports(statements);
|
|
8058
8289
|
for (const statement of statements) {
|
|
8059
8290
|
if (statement.type === 'ExportDefaultDeclaration') {
|
|
8060
8291
|
throw new Error('`module server` does not support default exports; use named functions.');
|
|
@@ -8111,7 +8342,15 @@ function analyzeServerModule(ast, filename) {
|
|
|
8111
8342
|
}
|
|
8112
8343
|
}
|
|
8113
8344
|
|
|
8114
|
-
|
|
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 };
|
|
8115
8354
|
}
|
|
8116
8355
|
|
|
8117
8356
|
/**
|
|
@@ -8129,15 +8368,19 @@ function emitServerModuleClientStubs(info, ctx) {
|
|
|
8129
8368
|
if (!imported || !local) continue;
|
|
8130
8369
|
ctx.runtimeNeeded.add('__serverRpc');
|
|
8131
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
|
+
];
|
|
8132
8381
|
nodes.push(
|
|
8133
8382
|
inheritOriginLoc(
|
|
8134
|
-
b.const(
|
|
8135
|
-
local,
|
|
8136
|
-
b.arrow(
|
|
8137
|
-
[b.rest(b.id('args'))],
|
|
8138
|
-
b.call('_$__serverRpc', b.literal(hash, JSON.stringify(hash)), b.id('args')),
|
|
8139
|
-
),
|
|
8140
|
-
),
|
|
8383
|
+
b.const(local, b.arrow([b.rest(b.id('args'))], b.call('_$__serverRpc', ...rpcArgs))),
|
|
8141
8384
|
specifier,
|
|
8142
8385
|
),
|
|
8143
8386
|
);
|
|
@@ -8204,13 +8447,29 @@ function emitServerModuleServerNodes(info, ctx) {
|
|
|
8204
8447
|
...namespaceBody,
|
|
8205
8448
|
];
|
|
8206
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
|
+
}
|
|
8207
8466
|
serverStatements.push(
|
|
8208
8467
|
inheritOriginLoc(
|
|
8209
8468
|
b.stmt(
|
|
8210
8469
|
b.assignment(
|
|
8211
8470
|
'=',
|
|
8212
8471
|
b.member(b.id('server'), b.literal(exported, JSON.stringify(exported)), true),
|
|
8213
|
-
|
|
8472
|
+
value,
|
|
8214
8473
|
),
|
|
8215
8474
|
),
|
|
8216
8475
|
origin,
|
|
@@ -8849,11 +9108,326 @@ export function compile(source, filename, options) {
|
|
|
8849
9108
|
return compileAuthored(source, filename, options, null);
|
|
8850
9109
|
}
|
|
8851
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
|
+
|
|
8852
9426
|
// Internal bundler entry point. Unlike the old __hydratePrepared handoff, this
|
|
8853
9427
|
// always validates authored source and only exposes the hydrate slice that the
|
|
8854
9428
|
// same compilation already prepared for production void-export classification.
|
|
8855
9429
|
export function compileForBundler(source, filename, options) {
|
|
8856
|
-
const metadata = { hydrateAst: null, cssModuleConstantImports: [] };
|
|
9430
|
+
const metadata = { hydrateAst: null, cssModuleConstantImports: [], independentWidgets: [] };
|
|
8857
9431
|
const result = compileAuthored(source, filename, options, metadata);
|
|
8858
9432
|
return { result, ...metadata };
|
|
8859
9433
|
}
|
|
@@ -8864,22 +9438,98 @@ function compileAuthored(source, filename, options, bundlerMetadata) {
|
|
|
8864
9438
|
throw new Error(`Unknown compile mode "${mode}" — expected 'client' or 'server'.`);
|
|
8865
9439
|
}
|
|
8866
9440
|
const cleanFilename = cleanCompileFilename(filename);
|
|
8867
|
-
|
|
9441
|
+
let analyzedAst = markParserSensitiveHosts(
|
|
8868
9442
|
normalizeAuthoredJsxLiterals(parseModule(source, cleanFilename)),
|
|
8869
9443
|
);
|
|
8870
9444
|
analyzeTsrx(analyzedAst, cleanFilename);
|
|
8871
9445
|
adoptParserAst(analyzedAst);
|
|
8872
9446
|
options = nativeReadOptions(analyzedAst, options);
|
|
8873
9447
|
assertNativeReadDiagnostics(analyzedAst, source, cleanFilename, options);
|
|
8874
|
-
const
|
|
8875
|
-
|
|
8876
|
-
|
|
8877
|
-
|
|
8878
|
-
|
|
8879
|
-
|
|
9448
|
+
const strongAnalysis = assertStrongMode(analyzedAst, source, cleanFilename, options);
|
|
9449
|
+
const strongModeEnabled = strongAnalysis?.enabled === true;
|
|
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
|
+
),
|
|
8880
9520
|
cleanFilename,
|
|
8881
|
-
options?.textTypeFacts,
|
|
8882
9521
|
);
|
|
9522
|
+
if (bundlerMetadata !== null) bundlerMetadata.hydrateAst = signalAst;
|
|
9523
|
+
const memoizedAst = strongModeEnabled
|
|
9524
|
+
? applyStrongAutomaticMemo(signalAst, {
|
|
9525
|
+
...options,
|
|
9526
|
+
filename: cleanFilename,
|
|
9527
|
+
strongHookAnalysis: strongAnalysis.strongHookAnalysis,
|
|
9528
|
+
})
|
|
9529
|
+
: signalAst;
|
|
9530
|
+
const textTypedAst = hasDomBindings
|
|
9531
|
+
? memoizedAst
|
|
9532
|
+
: applyStringChildProofs(memoizedAst, source, cleanFilename, options?.textTypeFacts);
|
|
8883
9533
|
let constantAst = textTypedAst;
|
|
8884
9534
|
if (typeof options?.resolveCssModuleConstant === 'function') {
|
|
8885
9535
|
const constants = applyCssModuleConstants(
|
|
@@ -8892,16 +9542,29 @@ function compileAuthored(source, filename, options, bundlerMetadata) {
|
|
|
8892
9542
|
bundlerMetadata.cssModuleConstantImports = constants.imports;
|
|
8893
9543
|
}
|
|
8894
9544
|
}
|
|
8895
|
-
|
|
9545
|
+
const result = compileInternal(
|
|
8896
9546
|
source,
|
|
8897
9547
|
filename,
|
|
8898
|
-
|
|
9548
|
+
strongAnalysis?.nativeChangeAnalysis
|
|
9549
|
+
? {
|
|
9550
|
+
...options,
|
|
9551
|
+
// Strong already analyzed the authored hosts. The classifications use
|
|
9552
|
+
// source offsets, which the copy-on-write transforms preserve.
|
|
9553
|
+
__nativeChangeAnalysis: strongAnalysis.nativeChangeAnalysis,
|
|
9554
|
+
__nativeChangeDiagnostics: strongAnalysis.nativeChangeAnalysis.diagnostics,
|
|
9555
|
+
}
|
|
9556
|
+
: options,
|
|
8899
9557
|
constantAst,
|
|
8900
9558
|
mode,
|
|
8901
9559
|
bundlerMetadata,
|
|
8902
|
-
textTypedAst !==
|
|
9560
|
+
textTypedAst !== memoizedAst,
|
|
8903
9561
|
strongModeEnabled,
|
|
8904
9562
|
);
|
|
9563
|
+
if (strongAnalysis?.diagnostics.length > 0) {
|
|
9564
|
+
result.diagnostics = [...strongAnalysis.diagnostics, ...(result.diagnostics ?? [])];
|
|
9565
|
+
}
|
|
9566
|
+
if (bindingConstants !== undefined) result.bindingConstants = bindingConstants;
|
|
9567
|
+
return result;
|
|
8905
9568
|
}
|
|
8906
9569
|
|
|
8907
9570
|
function compileInternal(
|
|
@@ -8992,6 +9655,7 @@ function compileInternal(
|
|
|
8992
9655
|
).diagnostics;
|
|
8993
9656
|
if (bundlerMetadata !== null) {
|
|
8994
9657
|
bundlerMetadata.hydrateAst = hydratePreparation.ast;
|
|
9658
|
+
bundlerMetadata.independentWidgets = hydratePreparation.independentWidgets ?? [];
|
|
8995
9659
|
}
|
|
8996
9660
|
const compiled = compileInternal(
|
|
8997
9661
|
source,
|
|
@@ -9341,6 +10005,8 @@ function compileInternal(
|
|
|
9341
10005
|
ownedCssInjections: new Set(),
|
|
9342
10006
|
componentOwners: [],
|
|
9343
10007
|
currentComponentOwner: null,
|
|
10008
|
+
signalBindingsUsed: ast._octaneSignalDeclarations === true,
|
|
10009
|
+
signalBindingsEager: ast._octaneSignalDeclarations === true,
|
|
9344
10010
|
currentComponentLocals: null, // Set<string> while compiling a component body; null otherwise
|
|
9345
10011
|
currentMapTemps: null, // expression temps (map receiver/method, folded condition) owned by this function
|
|
9346
10012
|
currentAutoMemoOffset: 0, // flat compiler-cache cell offset for the body being emitted
|
|
@@ -9871,18 +10537,18 @@ function compileInternal(
|
|
|
9871
10537
|
).filter((n) => n.type !== 'HeadHoist');
|
|
9872
10538
|
if (inheritSoleCompRoot(bodyNodes, ctx)) eligible = false;
|
|
9873
10539
|
}
|
|
9874
|
-
info.eligible = eligible;
|
|
10540
|
+
info.eligible = eligible && !compNode._octaneBindingView;
|
|
9875
10541
|
// Single-ELEMENT-root output: the component's body renders exactly one plain
|
|
9876
10542
|
// DOM element (not a component tag, fragment, or control-flow). Such a
|
|
9877
10543
|
// component self-delimits via that element on CLIENT mount — its
|
|
9878
10544
|
// `componentSlot` needs no `comp`/`/comp` markers (singleRoot path), exactly
|
|
9879
10545
|
// like a single-root `@for` item. (Output-shape based — independent of which
|
|
9880
10546
|
// hooks it calls.)
|
|
9881
|
-
info.singleRoot = singleHostComponentRoot(compNode);
|
|
10547
|
+
info.singleRoot = !compNode._octaneBindingView && singleHostComponentRoot(compNode);
|
|
9882
10548
|
// Transitive @if-arm form of the same proof — resolved by the fixed point
|
|
9883
10549
|
// below once every same-module callee an arm depends on is itself proven.
|
|
9884
10550
|
info.singleRootIfDeps =
|
|
9885
|
-
!info.singleRoot && isVoidJsxCodeBlockFunction(compNode)
|
|
10551
|
+
!compNode._octaneBindingView && !info.singleRoot && isVoidJsxCodeBlockFunction(compNode)
|
|
9886
10552
|
? collectSingleRootIfDeps(compNode.body.render, locals, ctx)
|
|
9887
10553
|
: null;
|
|
9888
10554
|
}
|
|
@@ -10442,6 +11108,22 @@ function compileInternal(
|
|
|
10442
11108
|
ctx.runtimeNeeded.add('__vtSeen');
|
|
10443
11109
|
vtHintNodes.push(inheritOriginLoc(b.stmt(b.call(rtAlias('__vtSeen'))), moduleOrigin));
|
|
10444
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
|
+
}
|
|
10445
11127
|
|
|
10446
11128
|
// All remaining authored memo sites have now received their real hook slots.
|
|
10447
11129
|
// Lower callable helpers, returned-JSX functions, explicit-slot calls and
|
|
@@ -10450,6 +11132,7 @@ function compileInternal(
|
|
|
10450
11132
|
// accidentally import DOM memo helpers for a universal renderer's scope.
|
|
10451
11133
|
let moduleBody = [
|
|
10452
11134
|
...nativeReadActivationNodes(ctx, moduleOrigin),
|
|
11135
|
+
...signalBindingNodes,
|
|
10453
11136
|
...vtHintNodes,
|
|
10454
11137
|
...delegateNodes,
|
|
10455
11138
|
...styleNodes,
|
|
@@ -10502,9 +11185,12 @@ function compileInternal(
|
|
|
10502
11185
|
};
|
|
10503
11186
|
const printed = printNodeWithMap(program, ctx);
|
|
10504
11187
|
|
|
10505
|
-
/** @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 }} */
|
|
10506
11189
|
const result = {
|
|
10507
11190
|
code: printed.code,
|
|
11191
|
+
...(ast._octaneSignalDeclarations === true || ctx.nativeReads
|
|
11192
|
+
? { streamedSignals: true }
|
|
11193
|
+
: null),
|
|
10508
11194
|
map: {
|
|
10509
11195
|
version: 3,
|
|
10510
11196
|
sources: [ctx.mapSourceName],
|
|
@@ -10625,6 +11311,8 @@ function compileServer(
|
|
|
10625
11311
|
nativeModuleStyles: options?.nativeReads === true && hasModuleStyleMaps(ast.body),
|
|
10626
11312
|
hmr: false, // SSR never hot-swaps in place; client/server production slot shapes stay aligned
|
|
10627
11313
|
dev: !!(options && options.dev),
|
|
11314
|
+
signalBindingsUsed: ast._octaneSignalDeclarations === true,
|
|
11315
|
+
signalBindingsEager: ast._octaneSignalDeclarations === true || options?.nativeReads === true,
|
|
10628
11316
|
// SSR MIRROR of the parallel-`use()` pipeline (docs/suspense-parallel-use-
|
|
10629
11317
|
// plan.md Phase 5): the same memoize (Pass A) + hoist/batch (Pass B)
|
|
10630
11318
|
// transforms run on server bodies, emitting `_$puMemo`/`_$puBatch` — the
|
|
@@ -10803,12 +11491,34 @@ function compileServer(
|
|
|
10803
11491
|
// the flush may register `hookSlots` as a needed runtime import.
|
|
10804
11492
|
flushTailHookSymbols(ctx);
|
|
10805
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
|
+
}
|
|
10806
11510
|
const runtimeImportNodes = buildRuntimeImportNodes(ctx, 'octane/server', ctx._moduleOrigin);
|
|
10807
11511
|
const helperNodes = hoistedHelperNodes(ctx);
|
|
10808
11512
|
const program = {
|
|
10809
11513
|
type: 'Program',
|
|
10810
11514
|
sourceType: 'module',
|
|
10811
|
-
body: [
|
|
11515
|
+
body: [
|
|
11516
|
+
...runtimeImportNodes,
|
|
11517
|
+
...nativeActivation,
|
|
11518
|
+
...signalActivation,
|
|
11519
|
+
...helperNodes,
|
|
11520
|
+
...bodyNodes,
|
|
11521
|
+
],
|
|
10812
11522
|
metadata: { path: [] },
|
|
10813
11523
|
start: ast.start,
|
|
10814
11524
|
end: ast.end,
|
|
@@ -10817,6 +11527,9 @@ function compileServer(
|
|
|
10817
11527
|
const printed = printNodeWithMap(program, ctx);
|
|
10818
11528
|
const result = {
|
|
10819
11529
|
code: printed.code,
|
|
11530
|
+
...(ast._octaneSignalDeclarations === true || ctx.nativeReads
|
|
11531
|
+
? { streamedSignals: true }
|
|
11532
|
+
: null),
|
|
10820
11533
|
map: {
|
|
10821
11534
|
version: 3,
|
|
10822
11535
|
sources: [ctx.mapSourceName],
|
|
@@ -10939,6 +11652,20 @@ function compileServerComponent(node, ctx) {
|
|
|
10939
11652
|
if (componentReferencedAboveDeclaration(ctx, node, name)) {
|
|
10940
11653
|
const declaration = isDefault ? b.export_default(fn) : isExported ? b.export(fn) : fn;
|
|
10941
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
|
+
}
|
|
10942
11669
|
if (warmNode !== null) {
|
|
10943
11670
|
nodes.push(
|
|
10944
11671
|
inheritOriginLoc(
|
|
@@ -10956,7 +11683,19 @@ function compileServerComponent(node, ctx) {
|
|
|
10956
11683
|
return nodes;
|
|
10957
11684
|
}
|
|
10958
11685
|
|
|
10959
|
-
|
|
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
|
+
}
|
|
10960
11699
|
const declaration = inheritOriginLoc(b.const(name, initializer), node);
|
|
10961
11700
|
const nodes = [
|
|
10962
11701
|
isExported && !isDefault ? inheritOriginLoc(b.export(declaration), node) : declaration,
|
|
@@ -11262,8 +12001,19 @@ function ssrCompileBodyWithMapTemps(
|
|
|
11262
12001
|
// needs no carrier. Other bodies can cross a component/value boundary, where
|
|
11263
12002
|
// the carrier distinguishes compiled HTML from authored text returns. This
|
|
11264
12003
|
// flag belongs to this body only; nested callbacks keep their own contract.
|
|
11265
|
-
|
|
11266
|
-
|
|
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
|
+
);
|
|
11267
12017
|
const origin =
|
|
11268
12018
|
node.loc != null
|
|
11269
12019
|
? node
|
|
@@ -11294,10 +12044,20 @@ function ssrCompileBodyWithMapTemps(
|
|
|
11294
12044
|
// flow — all serialize elements or `<!--[-->…<!--]-->` ranges
|
|
11295
12045
|
// that break text-node adjacency on their own)
|
|
11296
12046
|
function textAdjacencyKind(node, ctx) {
|
|
12047
|
+
if (
|
|
12048
|
+
node._octaneBindingText ||
|
|
12049
|
+
node._octaneBindingOpaque ||
|
|
12050
|
+
node._octaneBindingValue ||
|
|
12051
|
+
node._octaneBindingSlot
|
|
12052
|
+
)
|
|
12053
|
+
return 'other';
|
|
11297
12054
|
if (node.type !== 'Text') return 'other';
|
|
11298
12055
|
const lit = staticTextLiteral(node.expression);
|
|
11299
12056
|
if (lit !== null) return lit === '' ? 'empty' : 'static';
|
|
11300
|
-
return
|
|
12057
|
+
return isDirectSignalHandleExpression(node.expression) ||
|
|
12058
|
+
isKnownTextChildExpression(node.expression, ctx.knownStringChildLocals)
|
|
12059
|
+
? 'dyn'
|
|
12060
|
+
: 'other';
|
|
11301
12061
|
}
|
|
11302
12062
|
|
|
11303
12063
|
// Does the child at `i` have a text-producing sibling next to it (looking
|
|
@@ -11563,10 +12323,17 @@ function ssrEmitNode(
|
|
|
11563
12323
|
componentNs,
|
|
11564
12324
|
nlGuard = false,
|
|
11565
12325
|
) {
|
|
12326
|
+
if (node._octaneBindingValue || node._octaneBindingSlot)
|
|
12327
|
+
return ssrEmitTsrxExpression(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs);
|
|
11566
12328
|
switch (node.type) {
|
|
11567
12329
|
case 'Text': {
|
|
11568
12330
|
const expr = node.expression;
|
|
11569
|
-
if (
|
|
12331
|
+
if (
|
|
12332
|
+
!node._octaneBindingText &&
|
|
12333
|
+
expr &&
|
|
12334
|
+
expr.type === 'Literal' &&
|
|
12335
|
+
typeof expr.value === 'string'
|
|
12336
|
+
) {
|
|
11570
12337
|
if (ctx.dev && ctx._ssrNestingText === true) {
|
|
11571
12338
|
ctx.runtimeNeeded.add('ssrNestingText');
|
|
11572
12339
|
return ssrCall(
|
|
@@ -11604,30 +12371,44 @@ function ssrEmitNode(
|
|
|
11604
12371
|
// primitive to text) — mirrors Ripple's `{expr}` vs `{expr as string}`.
|
|
11605
12372
|
// rewriteHookCalls: a `use(thenable)` in this hole bypasses the setup
|
|
11606
12373
|
// rewrite, so key it here too (else it collides with sibling/nested use()).
|
|
11607
|
-
if (
|
|
12374
|
+
if (
|
|
12375
|
+
isDirectSignalHandleExpression(expr) ||
|
|
12376
|
+
isKnownTextChildExpression(expr, ctx.knownStringChildLocals)
|
|
12377
|
+
) {
|
|
11608
12378
|
// ssrTextPre = ssrText + the runtime leading-'\n' protection (the value
|
|
11609
12379
|
// isn't known at compile time here).
|
|
11610
12380
|
const fn =
|
|
11611
12381
|
ctx.dev && ctx._ssrNestingText === true
|
|
11612
12382
|
? 'ssrNestingText'
|
|
11613
|
-
: nlGuard
|
|
12383
|
+
: nlGuard && !node._octaneBindingText
|
|
11614
12384
|
? 'ssrTextPre'
|
|
11615
12385
|
: 'ssrText';
|
|
11616
12386
|
ctx.runtimeNeeded.add(fn);
|
|
11617
|
-
|
|
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;
|
|
11618
12392
|
}
|
|
11619
12393
|
// rewriteJsxValues lowers any JSX embedded in the expression (e.g.
|
|
11620
12394
|
// `{cond && <div/>}`, a ternary, a `.map(x => <Row/>)`) to printable
|
|
11621
12395
|
// createElement(...) descriptors — exactly like ssrEmitTsrxExpression and
|
|
11622
12396
|
// the client makeChildCall. Without it the raw JSX leaks into the emitted
|
|
11623
12397
|
// ssrChild(...) call as unparseable source.
|
|
11624
|
-
const childHelper =
|
|
12398
|
+
const childHelper = node._octaneBindingOpaque
|
|
12399
|
+
? 'ssrBindingChild'
|
|
12400
|
+
: nlGuard
|
|
12401
|
+
? 'ssrChildPre'
|
|
12402
|
+
: 'ssrChild';
|
|
11625
12403
|
ctx.runtimeNeeded.add(childHelper);
|
|
11626
12404
|
const childExpr = ssrCall(
|
|
11627
12405
|
childHelper,
|
|
11628
12406
|
[
|
|
11629
12407
|
resolveStyleExpr(rewriteJsxValues(rewriteHookCalls(expr, ctx, name), ctx), cssHash),
|
|
11630
12408
|
b.id('__s'),
|
|
12409
|
+
...(node._octaneBindingOpaque
|
|
12410
|
+
? [b.literal(bindingMarker(node._octaneBindingOpaque, 'o'))]
|
|
12411
|
+
: []),
|
|
11631
12412
|
],
|
|
11632
12413
|
node,
|
|
11633
12414
|
);
|
|
@@ -11708,7 +12489,9 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
11708
12489
|
rejectVoidElementContent(tag, node, ctx);
|
|
11709
12490
|
rejectTextareaValueChildren(tag, node, ctx);
|
|
11710
12491
|
rejectDangerouslySetInnerHTMLChildren(tag, node, ctx);
|
|
11711
|
-
const attrs =
|
|
12492
|
+
const attrs = expandKnownAttributeSpreads(
|
|
12493
|
+
node.attributes || node.openingElement?.attributes || [],
|
|
12494
|
+
);
|
|
11712
12495
|
const devFormActionAttribute =
|
|
11713
12496
|
ctx.dev && (tag === 'form' || tag === 'button' || tag === 'input')
|
|
11714
12497
|
? attrs.find(
|
|
@@ -11808,15 +12591,42 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
11808
12591
|
else directAttributeIdentities.add(identity);
|
|
11809
12592
|
}
|
|
11810
12593
|
const firstSpreadIdx = attrs.findIndex(
|
|
11811
|
-
(a) => a.type === 'SpreadAttribute' || a.type === 'JSXSpreadAttribute',
|
|
12594
|
+
(a) => !a.knownSource && (a.type === 'SpreadAttribute' || a.type === 'JSXSpreadAttribute'),
|
|
11812
12595
|
);
|
|
11813
|
-
|
|
11814
|
-
|
|
11815
|
-
|
|
11816
|
-
|
|
11817
|
-
|
|
11818
|
-
|
|
11819
|
-
|
|
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
|
+
}
|
|
12623
|
+
// Customized built-ins suppress native unknown-property diagnostics based on
|
|
12624
|
+
// the final `is` prop, regardless of where that prop appears in JSX. Resolve
|
|
12625
|
+
// directly authored DEV hosts through the existing whole-element attribute
|
|
12626
|
+
// snapshot so sibling validators see that final value before serialization.
|
|
12627
|
+
// Production keeps its original fully baked/direct-serializer paths.
|
|
12628
|
+
const resolveDevCustomizedHost =
|
|
12629
|
+
ctx.dev &&
|
|
11820
12630
|
(selfNs === 'html' || selfNs === 'opaque') &&
|
|
11821
12631
|
!tag.includes('-') &&
|
|
11822
12632
|
directAttributeIdentities.has('is');
|
|
@@ -11964,13 +12774,40 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
11964
12774
|
};
|
|
11965
12775
|
|
|
11966
12776
|
for (let attrI = 0; attrI < attrs.length; attrI++) {
|
|
11967
|
-
|
|
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
|
+
}
|
|
11968
12792
|
if (attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') {
|
|
11969
12793
|
ensureAttrPart();
|
|
12794
|
+
ctx.signalBindingsUsed = true;
|
|
11970
12795
|
ctx.runtimeNeeded.add('ssrAttrs');
|
|
11971
12796
|
ctx.runtimeNeeded.add('ssrSnapshotSpread');
|
|
11972
12797
|
const tmp = bindAttributeEvaluation(
|
|
11973
|
-
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
|
+
),
|
|
11974
12811
|
);
|
|
11975
12812
|
if (resolveFormControlsAcrossSpreads) {
|
|
11976
12813
|
if (tag !== 'textarea' && formControlPart === -1) {
|
|
@@ -12013,6 +12850,29 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12013
12850
|
}
|
|
12014
12851
|
if (attr.type !== 'Attribute' && attr.type !== 'JSXAttribute') continue;
|
|
12015
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
|
+
}
|
|
12016
12876
|
if (rawAttrName === 'key') {
|
|
12017
12877
|
bindDiscardedAttributeValue(attr.value);
|
|
12018
12878
|
continue;
|
|
@@ -12084,10 +12944,13 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12084
12944
|
const ctlInner =
|
|
12085
12945
|
val == null ? null : val.type === 'JSXExpressionContainer' ? val.expression : val;
|
|
12086
12946
|
if (tag === 'input') {
|
|
12087
|
-
|
|
12947
|
+
let ctlExpr =
|
|
12088
12948
|
ctlInner === null
|
|
12089
12949
|
? inheritOriginLoc(b.literal(true, 'true'), attr)
|
|
12090
12950
|
: tsrxExprNode(ctlInner, ctx, name, inlinedSubs);
|
|
12951
|
+
if (attrName === 'value' || attrName === 'checked') {
|
|
12952
|
+
ctlExpr = markDirectControl(ctlExpr, attr);
|
|
12953
|
+
}
|
|
12091
12954
|
if (formControlPart === -1) {
|
|
12092
12955
|
flush();
|
|
12093
12956
|
formControlPart = parts.length;
|
|
@@ -12102,11 +12965,12 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12102
12965
|
}
|
|
12103
12966
|
// textarea / select: value/defaultValue never serialize as attributes —
|
|
12104
12967
|
// captured for the content position (textarea) / projection scope (select).
|
|
12105
|
-
|
|
12968
|
+
let ctlExpr =
|
|
12106
12969
|
ctlInner === null
|
|
12107
12970
|
? inheritOriginLoc(b.literal(true, 'true'), attr)
|
|
12108
|
-
: tsrxExprNode(ctlInner, ctx, name, inlinedSubs)
|
|
12109
|
-
);
|
|
12971
|
+
: tsrxExprNode(ctlInner, ctx, name, inlinedSubs);
|
|
12972
|
+
if (attrName === 'value') ctlExpr = markDirectControl(ctlExpr, attr);
|
|
12973
|
+
ctlExpr = bindAttributeEvaluation(ctlExpr);
|
|
12110
12974
|
if (resolveFormControlsAcrossSpreads) {
|
|
12111
12975
|
if (tag === 'select' && formControlPart === -1) {
|
|
12112
12976
|
flush();
|
|
@@ -12115,6 +12979,9 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12115
12979
|
}
|
|
12116
12980
|
formControlSources.push(ssrDirectSource(ctx, attrName, ctlExpr, attr));
|
|
12117
12981
|
} else if (attrName === 'value') {
|
|
12982
|
+
if (directSignalControlSite !== null) {
|
|
12983
|
+
formControlSources.push(ssrDirectSource(ctx, attrName, ctlExpr, attr));
|
|
12984
|
+
}
|
|
12118
12985
|
ctlValue = ctlExpr;
|
|
12119
12986
|
ctlValueAttr = attr;
|
|
12120
12987
|
} else {
|
|
@@ -12238,6 +13105,8 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12238
13105
|
cssHash,
|
|
12239
13106
|
);
|
|
12240
13107
|
attrExpr = tsrxExprNode(attrInner, ctx, name, inlinedSubs);
|
|
13108
|
+
if (!(ctx.nativeReads && attrName === 'style'))
|
|
13109
|
+
attrExpr = ssrSignalValue(attrExpr, ctx, attr, true);
|
|
12241
13110
|
}
|
|
12242
13111
|
attrSources.push(
|
|
12243
13112
|
ssrDirectSource(
|
|
@@ -12279,6 +13148,13 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12279
13148
|
let inner = val.type === 'JSXExpressionContainer' ? val.expression : val;
|
|
12280
13149
|
|
|
12281
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
|
+
}
|
|
12282
13158
|
inner = resolveStyleExpr(inner, cssHash);
|
|
12283
13159
|
if (!isAfterSpread && inner.type === 'Literal' && typeof inner.value === 'string') {
|
|
12284
13160
|
bakeLit(` style="${escapeAttr(inner.value)}"`, attrName, attr.name, inner);
|
|
@@ -12297,13 +13173,11 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12297
13173
|
flush();
|
|
12298
13174
|
ctx.runtimeNeeded.add('ssrStyle');
|
|
12299
13175
|
registerAttrLoweringOrigin(ctx, attr.name, 'ssrStyle', null);
|
|
12300
|
-
|
|
12301
|
-
|
|
12302
|
-
|
|
12303
|
-
|
|
12304
|
-
|
|
12305
|
-
),
|
|
12306
|
-
);
|
|
13176
|
+
inner = tsrxExprNode(inner, ctx, name, inlinedSubs);
|
|
13177
|
+
if (ctx.nativeReads)
|
|
13178
|
+
inner = b.call(requireRuntimeForContext(ctx, 'readNativeDomStyle'), inner);
|
|
13179
|
+
else inner = ssrSignalStyleObject(inner, ctx, attr);
|
|
13180
|
+
parts.push(ssrCall('ssrStyle', [bindAttributeEvaluation(inner)], attr));
|
|
12307
13181
|
continue;
|
|
12308
13182
|
}
|
|
12309
13183
|
|
|
@@ -12385,7 +13259,9 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12385
13259
|
flush();
|
|
12386
13260
|
ctx.runtimeNeeded.add('ssrAttr');
|
|
12387
13261
|
registerAttrLoweringOrigin(ctx, attr.name, 'ssrAttr', attrName);
|
|
12388
|
-
const valueExpr = bindAttributeEvaluation(
|
|
13262
|
+
const valueExpr = bindAttributeEvaluation(
|
|
13263
|
+
ssrSignalValue(tsrxExprNode(inner, ctx, name, inlinedSubs), ctx, attr, true),
|
|
13264
|
+
);
|
|
12389
13265
|
if (
|
|
12390
13266
|
devFormActionSources !== null &&
|
|
12391
13267
|
(attrName === 'type' ||
|
|
@@ -12413,10 +13289,18 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12413
13289
|
);
|
|
12414
13290
|
parts.push(singleDirectAttribute);
|
|
12415
13291
|
}
|
|
13292
|
+
let signalControlAttrs = null;
|
|
13293
|
+
if (directSignalControlSite !== null) {
|
|
13294
|
+
ctx.runtimeNeeded.add('ssrSignalControlAttrs');
|
|
13295
|
+
signalControlAttrs = ssrCall('ssrSignalControlAttrs', [b.array(formControlSources)], node);
|
|
13296
|
+
}
|
|
12416
13297
|
if (formControlPart !== -1) {
|
|
12417
13298
|
if (tag === 'input') {
|
|
12418
13299
|
ctx.runtimeNeeded.add('ssrInputAttrs');
|
|
12419
|
-
|
|
13300
|
+
let inputAttrs = ssrCall('ssrInputAttrs', [b.array(formControlSources)], node);
|
|
13301
|
+
if (signalControlAttrs !== null) {
|
|
13302
|
+
inputAttrs = inheritOriginLoc(b.binary('+', inputAttrs, signalControlAttrs), node);
|
|
13303
|
+
}
|
|
12420
13304
|
// React serializes/coerces ordinary attributes before projecting the
|
|
12421
13305
|
// effective checked/value state. Expressions and spread getters have
|
|
12422
13306
|
// already run into temps in authored order; move only this serialization
|
|
@@ -12427,8 +13311,18 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12427
13311
|
parts.push(inputAttrs);
|
|
12428
13312
|
} else {
|
|
12429
13313
|
ctx.runtimeNeeded.add('ssrSelectAttrs');
|
|
12430
|
-
|
|
12431
|
-
|
|
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);
|
|
12432
13326
|
}
|
|
12433
13327
|
if (attrPart !== -1) {
|
|
12434
13328
|
ctx.runtimeNeeded.add('ssrAttrs');
|
|
@@ -12439,6 +13333,7 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12439
13333
|
b.literal(tag, JSON.stringify(tag)),
|
|
12440
13334
|
b.literal(selfNs, JSON.stringify(selfNs)),
|
|
12441
13335
|
b.literal(resolveFormControlsAcrossSpreads),
|
|
13336
|
+
...(ctx.nativeReads ? [b.id(requireRuntimeForContext(ctx, 'readNativeDomStyle'))] : []),
|
|
12442
13337
|
],
|
|
12443
13338
|
node,
|
|
12444
13339
|
);
|
|
@@ -12527,6 +13422,9 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12527
13422
|
} else if (
|
|
12528
13423
|
htmlSources.length === 0 &&
|
|
12529
13424
|
onlyChild0 !== null &&
|
|
13425
|
+
!onlyChild0._octaneBindingOpaque &&
|
|
13426
|
+
!onlyChild0._octaneBindingValue &&
|
|
13427
|
+
!onlyChild0._octaneBindingSlot &&
|
|
12530
13428
|
staticTextLiteral(onlyChild0.expression) === null &&
|
|
12531
13429
|
!isKnownTextChildExpression(onlyChild0.expression, ctx.knownStringChildLocals)
|
|
12532
13430
|
) {
|
|
@@ -12746,11 +13644,12 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12746
13644
|
|
|
12747
13645
|
function ssrEmitComponent(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs) {
|
|
12748
13646
|
registerOriginAlias(ctx, node.closingElement?.name, node.id || node.openingElement?.name);
|
|
13647
|
+
const invocationSite = componentInvocationSite(ctx, node);
|
|
12749
13648
|
// M3 inherit-range: consume the body-root flag ONCE, before this component's
|
|
12750
13649
|
// props/children compile below (they recurse into ssrEmitNodes/ssrCompileSub
|
|
12751
13650
|
// and must not inherit it). Set by ssrCompileBody only for the sole
|
|
12752
13651
|
// comp-call root of a `@{}` body — which is exactly this emit.
|
|
12753
|
-
const inherit = ctx._ssrInheritRoot === true;
|
|
13652
|
+
const inherit = ctx._ssrInheritRoot === true && !node._octaneBindingSite;
|
|
12754
13653
|
ctx._ssrInheritRoot = false;
|
|
12755
13654
|
// Capture before compiling attributes/children: nested subs temporarily mutate
|
|
12756
13655
|
// this context flag. Only this component's immediate children sub inherits the
|
|
@@ -12905,11 +13804,11 @@ function ssrEmitComponent(node, ctx, name, inlinedSubs, parentNs, cssHash, compo
|
|
|
12905
13804
|
// The direct mode-only Activity was lowered to ActivityStatement. Richer
|
|
12906
13805
|
// Activity configs and generic explicit-key + spread sites keep one ordered
|
|
12907
13806
|
// element config, matching createElement without changing simple call sites.
|
|
12908
|
-
ctx.runtimeNeeded.add('
|
|
13807
|
+
ctx.runtimeNeeded.add('createElementFromConfig');
|
|
12909
13808
|
ctx.runtimeNeeded.add('ssrChild');
|
|
12910
13809
|
const descriptor = ssrCall(
|
|
12911
|
-
'
|
|
12912
|
-
[compExpr, inheritOriginLoc(b.object(propNodes), node)],
|
|
13810
|
+
'createElementFromConfig',
|
|
13811
|
+
[b.literal(invocationSite), compExpr, inheritOriginLoc(b.object(propNodes), node)],
|
|
12913
13812
|
node,
|
|
12914
13813
|
);
|
|
12915
13814
|
const child = ssrCall('ssrChild', [descriptor, b.id('__s')], node);
|
|
@@ -12929,16 +13828,38 @@ function ssrEmitComponent(node, ctx, name, inlinedSubs, parentNs, cssHash, compo
|
|
|
12929
13828
|
args.push(
|
|
12930
13829
|
b.literal(componentNs, JSON.stringify(componentNs)),
|
|
12931
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),
|
|
12932
13840
|
);
|
|
12933
|
-
|
|
12934
|
-
|
|
12935
|
-
args.push(b.literal(
|
|
12936
|
-
} else if (inherit) {
|
|
12937
|
-
args.push(b.literal(true, 'true'));
|
|
13841
|
+
}
|
|
13842
|
+
if (node._octaneBindingSite) {
|
|
13843
|
+
args.push(b.literal(bindingMarker(node._octaneBindingSite, 'v')));
|
|
12938
13844
|
}
|
|
12939
13845
|
return ssrCall(helper, args, node);
|
|
12940
13846
|
}
|
|
12941
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
|
+
|
|
12942
13863
|
// ---------------------------------------------------------------------------
|
|
12943
13864
|
// Server control flow — @if/@for/@switch/@try lowered to HTML-string builders.
|
|
12944
13865
|
// Each branch/item/case body is compiled (via ssrCompileSub) into a server
|
|
@@ -13139,6 +14060,29 @@ function ssrEmitIf(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs)
|
|
|
13139
14060
|
node.alternate,
|
|
13140
14061
|
)
|
|
13141
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
|
+
}
|
|
13142
14086
|
return ssrCall(
|
|
13143
14087
|
'ssrBlock',
|
|
13144
14088
|
[
|
|
@@ -13204,6 +14148,7 @@ function ssrEmitActivity(node, ctx, name, inlinedSubs, parentNs, cssHash, compon
|
|
|
13204
14148
|
}
|
|
13205
14149
|
|
|
13206
14150
|
function ssrEmitFor(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs) {
|
|
14151
|
+
const bindingSite = node._octaneBindingSite;
|
|
13207
14152
|
// rewriteHookCalls: key any `use(thenable)` in the @for iterable expression.
|
|
13208
14153
|
const itemsExpr = rewriteHookCalls(node.right, ctx, name);
|
|
13209
14154
|
const mapCall = node.nativeArrayMap || null;
|
|
@@ -13307,22 +14252,37 @@ function ssrEmitFor(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs
|
|
|
13307
14252
|
node.index ? [b.id('__it'), b.id('__i')] : [b.id('__it')],
|
|
13308
14253
|
node,
|
|
13309
14254
|
);
|
|
13310
|
-
const sharedItemRange =
|
|
13311
|
-
|
|
13312
|
-
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
|
|
13313
14260
|
? ssrCall(
|
|
13314
14261
|
'ssrForItem',
|
|
13315
14262
|
[
|
|
13316
|
-
itemKey,
|
|
14263
|
+
bindingSite ? b.id('__bindingKeyValue') : itemKey,
|
|
13317
14264
|
b.id(itemSub.fnName),
|
|
13318
14265
|
b.id('__it'),
|
|
13319
14266
|
node.index ? b.id('__i') : ssrVoid(node),
|
|
13320
14267
|
b.id('__s'),
|
|
13321
|
-
b.literal(!sharedItemRange),
|
|
14268
|
+
b.literal(!sharedItemRange && !bindingSite),
|
|
14269
|
+
...(mapCall === null ? [] : [b.literal(true)]),
|
|
13322
14270
|
],
|
|
13323
14271
|
node,
|
|
13324
14272
|
)
|
|
13325
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
|
+
}
|
|
13326
14286
|
if (node.empty) ctx.runtimeNeeded.add('ssrArm');
|
|
13327
14287
|
if (itemNeedsIdentity) ctx.runtimeNeeded.add('ssrForItem');
|
|
13328
14288
|
// Render every item into one incrementally-built string. Avoid map().join():
|
|
@@ -13336,8 +14296,30 @@ function ssrEmitFor(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs
|
|
|
13336
14296
|
const index = b.id('__i');
|
|
13337
14297
|
const loopBody = b.block([
|
|
13338
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
|
+
: []),
|
|
13339
14308
|
b.stmt(b.assignment('+=', b.id('__html'), renderItem)),
|
|
13340
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);
|
|
13341
14323
|
const render = b.arrow(
|
|
13342
14324
|
[],
|
|
13343
14325
|
b.block([
|
|
@@ -13349,10 +14331,11 @@ function ssrEmitFor(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs
|
|
|
13349
14331
|
),
|
|
13350
14332
|
b.if(
|
|
13351
14333
|
b.binary('===', b.member(items, 'length'), b.literal(0)),
|
|
13352
|
-
b.return(
|
|
14334
|
+
b.return(forRange(emptyCall, false)),
|
|
13353
14335
|
null,
|
|
13354
14336
|
),
|
|
13355
|
-
...(itemNeedsIdentity && keyDeclaration !== null ? [keyDeclaration] : []),
|
|
14337
|
+
...((itemNeedsIdentity || bindingSite) && keyDeclaration !== null ? [keyDeclaration] : []),
|
|
14338
|
+
...(bindingSite ? [b.const('__bindingKeys', b.new(b.id('Set'), node))] : []),
|
|
13356
14339
|
b.let('__html', b.literal('')),
|
|
13357
14340
|
b.for(
|
|
13358
14341
|
b.let('__i', b.literal(0)),
|
|
@@ -13360,7 +14343,7 @@ function ssrEmitFor(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs
|
|
|
13360
14343
|
b.update('++', index),
|
|
13361
14344
|
loopBody,
|
|
13362
14345
|
),
|
|
13363
|
-
b.return(
|
|
14346
|
+
b.return(forRange(b.id('__html'), true)),
|
|
13364
14347
|
]),
|
|
13365
14348
|
);
|
|
13366
14349
|
const fast = ssrCall('ssrControl', [b.literal(ssrControlKey('for', node)), render], node);
|
|
@@ -13570,6 +14553,33 @@ function ssrControlKey(kind, node) {
|
|
|
13570
14553
|
// renderer component so its template scopes and hydration range both survive.
|
|
13571
14554
|
function ssrEmitTsrxExpression(node, ctx, name, inlinedSubs, parentNs, cssHash, componentNs) {
|
|
13572
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
|
+
}
|
|
13573
14583
|
if (node.returnedJsxValue === true && requiresTemplateNormalization(expr, 'html', true, ctx)) {
|
|
13574
14584
|
// A returned JSX value that contains template-only syntax is one compiled
|
|
13575
14585
|
// renderer on the client (rather than a descriptor array whose value
|
|
@@ -13594,7 +14604,19 @@ function ssrEmitTsrxExpression(node, ctx, name, inlinedSubs, parentNs, cssHash,
|
|
|
13594
14604
|
);
|
|
13595
14605
|
inlinedSubs.push(sub.fn);
|
|
13596
14606
|
ctx.runtimeNeeded.add('ssrComponent');
|
|
13597
|
-
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
|
+
);
|
|
13598
14620
|
}
|
|
13599
14621
|
if (
|
|
13600
14622
|
expr &&
|
|
@@ -13624,10 +14646,7 @@ function ssrEmitTsrxExpression(node, ctx, name, inlinedSubs, parentNs, cssHash,
|
|
|
13624
14646
|
// position — body/arm/fragment roots and component-children roots,
|
|
13625
14647
|
// where a rich hole rides the de-opt value path because a portal there
|
|
13626
14648
|
// has no host to stamp (`_ssrHostChildPos`).
|
|
13627
|
-
const asIf =
|
|
13628
|
-
...b.if(expr.test, wrapAsBlockStmt(expr.consequent), wrapAsBlockStmt(expr.alternate)),
|
|
13629
|
-
loc: expr.loc, // same devLoc/control-key position as the client's claim
|
|
13630
|
-
};
|
|
14649
|
+
const asIf = conditionalJsxStatement(expr);
|
|
13631
14650
|
return ssrEmitIf(asIf, ctx, name, inlinedSubs, parentNs, cssHash, componentNs);
|
|
13632
14651
|
}
|
|
13633
14652
|
ctx.runtimeNeeded.add('ssrChild');
|
|
@@ -14100,6 +15119,17 @@ function compileComponent(node, ctx, options) {
|
|
|
14100
15119
|
// back to a full reload — the capture predates the wrapper).
|
|
14101
15120
|
statements.push(guarded(b.assignment('=', b.id(name), b.call('_$hmr', b.id(name)))));
|
|
14102
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
|
+
}
|
|
14103
15133
|
return { nodes: statements };
|
|
14104
15134
|
}
|
|
14105
15135
|
|
|
@@ -14129,6 +15159,18 @@ function compileComponent(node, ctx, options) {
|
|
|
14129
15159
|
// its fresh body to the previous canonical wrapper and re-export that same
|
|
14130
15160
|
// identity. Production components retain their tree-shakeable const binding.
|
|
14131
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
|
+
}
|
|
14132
15174
|
if (!hmrWrap && componentInfo?.singleRoot === true) {
|
|
14133
15175
|
valueExpr = inheritOriginLoc(singleRootInitializer(ctx, valueExpr), node);
|
|
14134
15176
|
componentInfo.singleRootInitialized = true;
|
|
@@ -14159,18 +15201,204 @@ function compileComponent(node, ctx, options) {
|
|
|
14159
15201
|
return { nodes: [declNode] };
|
|
14160
15202
|
}
|
|
14161
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
|
+
|
|
14162
15394
|
/**
|
|
14163
|
-
* Generate
|
|
14164
|
-
*
|
|
14165
|
-
*
|
|
14166
|
-
* `parentNs` is the namespace this body's JSX is rendered into. Top-level
|
|
14167
|
-
* components use 'opaque' because their call site can select HTML, SVG, or
|
|
14168
|
-
* MathML; an if/for/try body inherits its known host namespace.
|
|
14169
|
-
*
|
|
14170
|
-
* `cssHash` is the enclosing component's scoped-style hash (or null) — used to
|
|
14171
|
-
* 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.
|
|
14172
15398
|
*/
|
|
14173
15399
|
function compileFunctionBody(node, ctx, name, parentNs = 'html', cssHash = null, options = null) {
|
|
15400
|
+
const previousPresentationHydration = ctx.presentationHydration;
|
|
15401
|
+
ctx.presentationHydration = node._octanePresentationHydration;
|
|
14174
15402
|
const returnedOutput = options?.returnedOutput === true;
|
|
14175
15403
|
const previousAutoCalculatedRenderableRefs = ctx.currentAutoCalculatedRenderableRefs;
|
|
14176
15404
|
let autoCalculatedDeclarations = null;
|
|
@@ -14650,7 +15878,7 @@ function compileFunctionBody(node, ctx, name, parentNs = 'html', cssHash = null,
|
|
|
14650
15878
|
b.member(b.member(b.id('__s'), 'block'), 'pending'),
|
|
14651
15879
|
b.unary('!', b.member(b.member(b.id('__s'), 'block'), 'crossRenderUpdate')),
|
|
14652
15880
|
),
|
|
14653
|
-
b.return(null),
|
|
15881
|
+
{ ...b.return(null), _octanePendingOutputGuard: true },
|
|
14654
15882
|
null,
|
|
14655
15883
|
),
|
|
14656
15884
|
node,
|
|
@@ -14773,13 +16001,15 @@ function compileFunctionBody(node, ctx, name, parentNs = 'html', cssHash = null,
|
|
|
14773
16001
|
ctx.currentMapTemps = prevMapTemps;
|
|
14774
16002
|
// ONE FunctionDeclaration node — the caller prints it (once, with the full
|
|
14775
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;
|
|
14776
16006
|
const emittedFunction = b.function_declaration(
|
|
14777
16007
|
b.id(name, node.id ?? node),
|
|
14778
16008
|
fnParams,
|
|
14779
16009
|
b.block(
|
|
14780
16010
|
ctx.nativeReads
|
|
14781
|
-
? wrapNativeReadScope(
|
|
14782
|
-
:
|
|
16011
|
+
? wrapNativeReadScope(presentationBody, b.id('__s'), nativeReadNames(ctx))
|
|
16012
|
+
: presentationBody,
|
|
14783
16013
|
),
|
|
14784
16014
|
);
|
|
14785
16015
|
return inheritOriginLoc(
|
|
@@ -15351,6 +16581,10 @@ function collectDepPaths(expr) {
|
|
|
15351
16581
|
return;
|
|
15352
16582
|
}
|
|
15353
16583
|
switch (n.type) {
|
|
16584
|
+
case 'MetaProperty':
|
|
16585
|
+
// `import.meta` and `new.target` contain syntax tokens, not free
|
|
16586
|
+
// bindings. Visiting their Identifier children creates invalid deps.
|
|
16587
|
+
return;
|
|
15354
16588
|
case 'Identifier':
|
|
15355
16589
|
if (!bound.has(n.name)) push(b.id(n.name), n.name);
|
|
15356
16590
|
return;
|
|
@@ -18219,10 +19453,29 @@ function compileReturnJsxFunction(node, ctx, options) {
|
|
|
18219
19453
|
// export follow-up statements — compile() embeds them in the single module
|
|
18220
19454
|
// AST, whose one esrap print yields the whole module map (chained consumers
|
|
18221
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
|
+
: [];
|
|
18222
19474
|
if (options && options.hmrWrap) {
|
|
18223
19475
|
const nodes = [
|
|
18224
19476
|
fn,
|
|
18225
19477
|
inheritOriginLoc(b.stmt(b.assignment('=', b.id(name), b.call('_$hmr', b.id(name)))), node),
|
|
19478
|
+
...bindingStamp,
|
|
18226
19479
|
];
|
|
18227
19480
|
if (options.default) {
|
|
18228
19481
|
nodes.push(
|
|
@@ -18239,10 +19492,11 @@ function compileReturnJsxFunction(node, ctx, options) {
|
|
|
18239
19492
|
return { nodes };
|
|
18240
19493
|
}
|
|
18241
19494
|
if (options && options.default) {
|
|
18242
|
-
return { nodes: [fn, inheritOriginLoc(b.export_default(b.id(name)), node)] };
|
|
19495
|
+
return { nodes: [fn, ...bindingStamp, inheritOriginLoc(b.export_default(b.id(name)), node)] };
|
|
18243
19496
|
}
|
|
18244
|
-
if (options && options.export)
|
|
18245
|
-
|
|
19497
|
+
if (options && options.export)
|
|
19498
|
+
return { nodes: [inheritOriginLoc(b.export(fn), node), ...bindingStamp] };
|
|
19499
|
+
return { nodes: [fn, ...bindingStamp] };
|
|
18246
19500
|
}
|
|
18247
19501
|
|
|
18248
19502
|
function nativeReturnedJsxValue(
|
|
@@ -18719,6 +19973,23 @@ function extractFragment(node, ctx, holeProps, parentNs = 'html') {
|
|
|
18719
19973
|
newAttrs.push(attr);
|
|
18720
19974
|
continue;
|
|
18721
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
|
+
}
|
|
18722
19993
|
const v = attr.value;
|
|
18723
19994
|
const inner = v && v.type === 'JSXExpressionContainer' ? v.expression : v;
|
|
18724
19995
|
// Dynamic attr = an expression value that isn't a static literal/string.
|
|
@@ -18787,9 +20058,10 @@ function extractFragment(node, ctx, holeProps, parentNs = 'html') {
|
|
|
18787
20058
|
const annotation = isKnownTextChildExpression(expr, ctx.knownStringChildLocals)
|
|
18788
20059
|
? b.ts_keyword_type('string')
|
|
18789
20060
|
: expr.typeAnnotation;
|
|
18790
|
-
newChildren.push(
|
|
18791
|
-
|
|
18792
|
-
|
|
20061
|
+
newChildren.push({
|
|
20062
|
+
...child,
|
|
20063
|
+
expression: b.ts_as(memberProps(hn, expr.expression), annotation),
|
|
20064
|
+
});
|
|
18793
20065
|
} else {
|
|
18794
20066
|
holeProps.push(objectProp(hn, rewriteExtractedFragmentHole(expr, ctx, childNs)));
|
|
18795
20067
|
// The renderer only sees `props.hN`, so it cannot rediscover an inline
|
|
@@ -18800,15 +20072,16 @@ function extractFragment(node, ctx, holeProps, parentNs = 'html') {
|
|
|
18800
20072
|
const rendered = isKnownTextChildExpression(expr, ctx.knownStringChildLocals)
|
|
18801
20073
|
? b.ts_as(member, b.ts_keyword_type('string'))
|
|
18802
20074
|
: member;
|
|
18803
|
-
newChildren.push(
|
|
20075
|
+
newChildren.push({ ...child, expression: rendered });
|
|
18804
20076
|
}
|
|
18805
20077
|
} else if (t === 'Element' || t === 'JSXElement') {
|
|
18806
20078
|
if (isLongFormTemplateSentinel(child, childNs, true, ctx)) {
|
|
18807
20079
|
newChildren.push(extractFragment(child, ctx, holeProps, childNs));
|
|
18808
20080
|
} else if (
|
|
18809
20081
|
isComponentTag(child) &&
|
|
18810
|
-
|
|
18811
|
-
|
|
20082
|
+
(child._octaneBindingSite ||
|
|
20083
|
+
(ctx._foldCtx?.templateComponentChildren === true &&
|
|
20084
|
+
requiresTemplateNormalization(child, childNs, true, ctx)))
|
|
18812
20085
|
) {
|
|
18813
20086
|
// Component children normally become descriptor children at return-value
|
|
18814
20087
|
// position. A directive cannot be represented by lowerJsxChild, though, so
|
|
@@ -18877,7 +20150,12 @@ function extractFragment(node, ctx, holeProps, parentNs = 'html') {
|
|
|
18877
20150
|
// before fragment extraction. It is still an owner-side renderable hole.
|
|
18878
20151
|
const hn = `h${holeProps.length}`;
|
|
18879
20152
|
holeProps.push(objectProp(hn, rewriteExtractedFragmentHole(child.expression, ctx, childNs)));
|
|
18880
|
-
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
|
+
});
|
|
18881
20159
|
} else if ((t === 'IfStatement' || t === 'JSXIfExpression') && ctx._foldCtx) {
|
|
18882
20160
|
// FOLD a directive: lower its branch bodies on the COMPONENT side (so the
|
|
18883
20161
|
// `__then$N`/`__else$N` helpers keep their closure over setup locals/props),
|
|
@@ -18894,7 +20172,12 @@ function extractFragment(node, ctx, holeProps, parentNs = 'html') {
|
|
|
18894
20172
|
inheritOriginLoc(b.if(child.test, child.consequent, child.alternate || null), child),
|
|
18895
20173
|
// The clause keyword's own span — `@else` starts before its
|
|
18896
20174
|
// block, so it is the only authored range that names the arm.
|
|
18897
|
-
{
|
|
20175
|
+
{
|
|
20176
|
+
alternateKeyword: child.alternateKeyword ?? null,
|
|
20177
|
+
...(child._octaneBindingSite
|
|
20178
|
+
? { _octaneBindingSite: child._octaneBindingSite }
|
|
20179
|
+
: {}),
|
|
20180
|
+
},
|
|
18898
20181
|
)
|
|
18899
20182
|
: child;
|
|
18900
20183
|
const ic = makeIfCall(ifNode, ctx, fc.compInlinedSubs, fc.parentNs, fc.cssHash);
|
|
@@ -18977,6 +20260,9 @@ function extractFragment(node, ctx, holeProps, parentNs = 'html') {
|
|
|
18977
20260
|
index: child.index || null,
|
|
18978
20261
|
empty: child.empty || null,
|
|
18979
20262
|
emptyKeyword: child.emptyKeyword ?? null,
|
|
20263
|
+
...(child._octaneBindingSite
|
|
20264
|
+
? { _octaneBindingSite: child._octaneBindingSite }
|
|
20265
|
+
: {}),
|
|
18980
20266
|
},
|
|
18981
20267
|
)
|
|
18982
20268
|
: child;
|
|
@@ -19284,9 +20570,17 @@ function lowerHostFragment(
|
|
|
19284
20570
|
} else {
|
|
19285
20571
|
ctx.hoistedHelpers.push(renderer);
|
|
19286
20572
|
}
|
|
19287
|
-
ctx.runtimeNeeded.add('
|
|
20573
|
+
ctx.runtimeNeeded.add('createElementFromConfig');
|
|
19288
20574
|
// The fragment-renderer call maps to the authored fragment root.
|
|
19289
|
-
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
|
+
);
|
|
19290
20584
|
}
|
|
19291
20585
|
|
|
19292
20586
|
/**
|
|
@@ -19591,7 +20885,7 @@ function serverValueDirectiveFold(
|
|
|
19591
20885
|
directive,
|
|
19592
20886
|
),
|
|
19593
20887
|
);
|
|
19594
|
-
ctx.runtimeNeeded.add('
|
|
20888
|
+
ctx.runtimeNeeded.add('createElementFromConfig');
|
|
19595
20889
|
// The wrapper call stands where the directive was authored, and every printed
|
|
19596
20890
|
// node needs an origin (assertNodeLocs). Inside a component body the enclosing
|
|
19597
20891
|
// statement supplied one; a module-level statement does not.
|
|
@@ -19603,7 +20897,12 @@ function serverValueDirectiveFold(
|
|
|
19603
20897
|
);
|
|
19604
20898
|
}
|
|
19605
20899
|
return inheritOriginLoc(
|
|
19606
|
-
b.call(
|
|
20900
|
+
b.call(
|
|
20901
|
+
'_$createElementFromConfig',
|
|
20902
|
+
b.literal(componentInvocationSite(ctx, directive)),
|
|
20903
|
+
b.id(wrapperName),
|
|
20904
|
+
b.object(descriptorProps),
|
|
20905
|
+
),
|
|
19607
20906
|
directive,
|
|
19608
20907
|
);
|
|
19609
20908
|
};
|
|
@@ -20144,6 +21443,7 @@ function jsxElementToCreateElement(node, ctx, eagerRoot = false) {
|
|
|
20144
21443
|
const nameNode = node.openingElement?.name || node.id;
|
|
20145
21444
|
const activity = isActivityLongForm(node, ctx);
|
|
20146
21445
|
const componentTag = activity || isComponentTag(node);
|
|
21446
|
+
const invocationSite = componentTag ? componentInvocationSite(ctx, node) : null;
|
|
20147
21447
|
// Host (lowercase) tag → string literal (`'li'`) for the de-opt renderer;
|
|
20148
21448
|
// component (capitalized / member / dynamic) → the identifier/member ref.
|
|
20149
21449
|
const compNode = activity
|
|
@@ -20161,6 +21461,15 @@ function jsxElementToCreateElement(node, ctx, eagerRoot = false) {
|
|
|
20161
21461
|
const attrs = node.attributes || node.openingElement?.attributes || [];
|
|
20162
21462
|
const properties = [];
|
|
20163
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
|
+
);
|
|
20164
21473
|
if (attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') {
|
|
20165
21474
|
properties.push(b.spread(rewriteJsxValues(attr.argument, ctx)));
|
|
20166
21475
|
continue;
|
|
@@ -20217,7 +21526,21 @@ function jsxElementToCreateElement(node, ctx, eagerRoot = false) {
|
|
|
20217
21526
|
);
|
|
20218
21527
|
}
|
|
20219
21528
|
}
|
|
20220
|
-
const
|
|
21529
|
+
const propsObject = inheritOriginLoc(b.object(properties), node);
|
|
21530
|
+
const propsNode =
|
|
21531
|
+
ctx.nativeReads &&
|
|
21532
|
+
!componentTag &&
|
|
21533
|
+
attrs.some(
|
|
21534
|
+
(attr) =>
|
|
21535
|
+
attr.type === 'SpreadAttribute' ||
|
|
21536
|
+
attr.type === 'JSXSpreadAttribute' ||
|
|
21537
|
+
(attr.name?.name ?? attr.name) === 'style',
|
|
21538
|
+
)
|
|
21539
|
+
? inheritOriginLoc(
|
|
21540
|
+
b.call(requireRuntimeForContext(ctx, 'readNativeDomProps'), propsObject),
|
|
21541
|
+
node,
|
|
21542
|
+
)
|
|
21543
|
+
: propsObject;
|
|
20221
21544
|
const childrenNeedRenderScope = jsxValueChildrenNeedRenderScope(node);
|
|
20222
21545
|
const eagerProviderChildren =
|
|
20223
21546
|
eagerRoot &&
|
|
@@ -20344,13 +21667,30 @@ function jsxElementToCreateElement(node, ctx, eagerRoot = false) {
|
|
|
20344
21667
|
? requireRuntimeForContext(ctx, 'nativeCreateScopedElement')
|
|
20345
21668
|
: rtAlias('createScopedElement');
|
|
20346
21669
|
const readChildren = inheritOriginLoc(b.arrow([], memoizedChildrenBody ?? childrenValue), node);
|
|
20347
|
-
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
|
+
);
|
|
20348
21680
|
} else {
|
|
20349
|
-
ctx.runtimeNeeded.add('createElement');
|
|
21681
|
+
ctx.runtimeNeeded.add(componentTag ? 'createElementFromConfig' : 'createElement');
|
|
20350
21682
|
// Remaining scaffolding (callee, props object, spread/diagnostic wrappers,
|
|
20351
21683
|
// static-content literals) maps to the authored JSX element.
|
|
20352
21684
|
descriptor = inheritOriginLoc(
|
|
20353
|
-
|
|
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),
|
|
20354
21694
|
node,
|
|
20355
21695
|
);
|
|
20356
21696
|
}
|
|
@@ -20994,6 +22334,14 @@ function rewriteOpaqueTitles(node, ctx, namespace = 'html') {
|
|
|
20994
22334
|
function headAttrsExpression(el) {
|
|
20995
22335
|
const attrProps = [];
|
|
20996
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
|
+
);
|
|
20997
22345
|
if (a.type === 'SpreadAttribute' || a.type === 'JSXSpreadAttribute') {
|
|
20998
22346
|
attrProps.push(b.spread(a.argument));
|
|
20999
22347
|
continue;
|
|
@@ -21377,6 +22725,14 @@ function normalizeChildren(
|
|
|
21377
22725
|
out.push({
|
|
21378
22726
|
type: needsRichDispatch(expression) ? 'TSRXExpression' : 'Text',
|
|
21379
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 }),
|
|
21380
22736
|
});
|
|
21381
22737
|
} else if (n.type === 'JSXElement') {
|
|
21382
22738
|
// Long-form `<Fragment>…</Fragment>` (canary `enableFragmentRefs`
|
|
@@ -21493,6 +22849,7 @@ function normalizeChildren(
|
|
|
21493
22849
|
selfClosing: n.openingElement.selfClosing,
|
|
21494
22850
|
loc: n.loc, // preserve element position for dev hydration LOC (component slots)
|
|
21495
22851
|
};
|
|
22852
|
+
if (n._octaneBindingSite !== undefined) element._octaneBindingSite = n._octaneBindingSite;
|
|
21496
22853
|
// `unstable_Activity` starts with a lowercase letter but is a component
|
|
21497
22854
|
// when it resolves to the builtin. Keep that fact on this rare node only.
|
|
21498
22855
|
if (isActivityLongForm(n, ctx)) element.activityDescriptor = true;
|
|
@@ -21544,6 +22901,9 @@ function normalizeChildren(
|
|
|
21544
22901
|
out.push(
|
|
21545
22902
|
Object.assign(inheritOriginLoc(b.if(n.test, n.consequent, n.alternate || null), n), {
|
|
21546
22903
|
alternateKeyword: n.alternateKeyword ?? null,
|
|
22904
|
+
...(n._octaneBindingSite === undefined
|
|
22905
|
+
? {}
|
|
22906
|
+
: { _octaneBindingSite: n._octaneBindingSite }),
|
|
21547
22907
|
}),
|
|
21548
22908
|
);
|
|
21549
22909
|
} else if (n.type === 'JSXForExpression') {
|
|
@@ -21557,6 +22917,9 @@ function normalizeChildren(
|
|
|
21557
22917
|
index: n.index || null,
|
|
21558
22918
|
empty: n.empty || null,
|
|
21559
22919
|
emptyKeyword: n.emptyKeyword ?? null,
|
|
22920
|
+
...(n._octaneBindingSite === undefined
|
|
22921
|
+
? {}
|
|
22922
|
+
: { _octaneBindingSite: n._octaneBindingSite }),
|
|
21560
22923
|
}),
|
|
21561
22924
|
);
|
|
21562
22925
|
} else if (n.type === 'JSXTryExpression') {
|
|
@@ -22278,8 +23641,24 @@ function stripTsOnlyWrappers(node) {
|
|
|
22278
23641
|
function nativeReadActivationNodes(ctx, origin) {
|
|
22279
23642
|
// Install the graph-free driver before any authored root invocation, not
|
|
22280
23643
|
// after parameter evaluation inside a syntactically recognized component.
|
|
22281
|
-
//
|
|
22282
|
-
|
|
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 [];
|
|
22283
23662
|
return [
|
|
22284
23663
|
inheritOriginLoc(
|
|
22285
23664
|
b.stmt(b.call(requireRuntimeForContext(ctx, 'enableNativeReadCollection'), b.literal(1))),
|
|
@@ -22874,7 +24253,7 @@ function planJsx(
|
|
|
22874
24253
|
// A dynamic text hole with a text-producing neighbour: the server emits a
|
|
22875
24254
|
// `<!-- -->` separator between the two texts (else the parser would merge
|
|
22876
24255
|
// them), and only the hole-aware sibling() walk knows to step across it.
|
|
22877
|
-
elementBindings.some((b) => b.kind === 'text' && b.adjacentText);
|
|
24256
|
+
elementBindings.some((b) => b.kind === 'text' && (b.adjacentText || b.bindingMarker));
|
|
22878
24257
|
if (hasHoles) {
|
|
22879
24258
|
ctx.runtimeNeeded.add('child');
|
|
22880
24259
|
ctx.runtimeNeeded.add('sibling');
|
|
@@ -23049,6 +24428,7 @@ function planJsx(
|
|
|
23049
24428
|
// happens on the intact template. (Regression: StoryRow's `.meta` interleaves text holes
|
|
23050
24429
|
// with `<Link>` components.)
|
|
23051
24430
|
const deferredTextMounts = [];
|
|
24431
|
+
let nativeStyleSlots = 0;
|
|
23052
24432
|
// Emit per-binding mount code.
|
|
23053
24433
|
for (const b of elementBindings) {
|
|
23054
24434
|
// A sibling-position `{x as string}` text hole resolves to its POSITION node
|
|
@@ -23063,14 +24443,21 @@ function planJsx(
|
|
|
23063
24443
|
// htext/htextSwap/setAttribute (which only have the element) can report `file:line:col`.
|
|
23064
24444
|
if (_locStamped && b.kind !== 'text') stampHostLoc(elVar, b.path, undefined);
|
|
23065
24445
|
else if (_locStamped) stampHostLoc(ensureVar(b.path), b.path, undefined);
|
|
23066
|
-
|
|
23067
|
-
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');
|
|
23068
24455
|
if (b.kind === 'textOnlyChild') ctx.runtimeNeeded.add('htext');
|
|
23069
24456
|
// Mounts use the unconditional writer; reactive scalar bindings use either
|
|
23070
24457
|
// that writer behind an inline guard or its compact comparison helper.
|
|
23071
24458
|
// Claim both authored-name tokens when both paths exist, and do not retain
|
|
23072
24459
|
// a mount-only import for deferred writes.
|
|
23073
|
-
const attrHelper = attrBindingHelper(b);
|
|
24460
|
+
const attrHelper = b.signalDirect ? null : attrBindingHelper(b);
|
|
23074
24461
|
if (attrHelper !== null) {
|
|
23075
24462
|
if (b.kind === 'class' && b.fresh && (!b.deferred || (!b.mountOnly && inlineBindingGuards)))
|
|
23076
24463
|
ctx.runtimeNeeded.add('normalizeClass');
|
|
@@ -23088,8 +24475,12 @@ function planJsx(
|
|
|
23088
24475
|
if (b.kind === 'dangerCommit') ctx.runtimeNeeded.add('setDangerouslySetInnerHTMLSources');
|
|
23089
24476
|
if (b.kind === 'formCommit') ctx.runtimeNeeded.add('setFormControlSources');
|
|
23090
24477
|
if (b.kind === 'hostCommit') {
|
|
23091
|
-
ctx.
|
|
23092
|
-
ctx.runtimeNeeded.add('
|
|
24478
|
+
if (ctx.nativeReads) b.readStyle = requireRuntimeForContext(ctx, 'readNativeDomStyle');
|
|
24479
|
+
if (b.signalHostSources) ctx.runtimeNeeded.add('bindSignalHostPropSources');
|
|
24480
|
+
else {
|
|
24481
|
+
ctx.runtimeNeeded.add('setHostPropSources');
|
|
24482
|
+
ctx.runtimeNeeded.add('queueOwnRefDetach');
|
|
24483
|
+
}
|
|
23093
24484
|
}
|
|
23094
24485
|
// A commit-phase collector takes the element's props as one grouped call,
|
|
23095
24486
|
// so each source's name literal — not the shared helper — is what claims
|
|
@@ -23121,7 +24512,15 @@ function planJsx(
|
|
|
23121
24512
|
}
|
|
23122
24513
|
if (b.kind === 'styleProperties') {
|
|
23123
24514
|
ctx.runtimeNeeded.add('setStyleProperty');
|
|
23124
|
-
ctx.runtimeNeeded.add('isHydratingStyle');
|
|
24515
|
+
ctx.runtimeNeeded.add(b.spread ? 'canSplitStyleProperties' : 'isHydratingStyle');
|
|
24516
|
+
if (b.spread) ctx.runtimeNeeded.add('styleObjectPrototype');
|
|
24517
|
+
}
|
|
24518
|
+
if (b.kind === 'nativeStyle' || b.kind === 'nativeProjection') {
|
|
24519
|
+
b.slotIndex = ++nativeStyleSlots;
|
|
24520
|
+
b.helper = requireRuntimeForContext(
|
|
24521
|
+
ctx,
|
|
24522
|
+
b.kind === 'nativeStyle' ? 'nativeStyleBinding' : 'nativeProjectionBinding',
|
|
24523
|
+
);
|
|
23125
24524
|
}
|
|
23126
24525
|
if (b.kind === 'spread') {
|
|
23127
24526
|
ctx.runtimeNeeded.add('setSpread');
|
|
@@ -23383,7 +24782,8 @@ function planJsx(
|
|
|
23383
24782
|
const afterCalls = [];
|
|
23384
24783
|
const pushAfter = (id, line) => afterCalls.push({ id, line });
|
|
23385
24784
|
// Dense per-body slot indices. Slot 0 is this body's binding bag (`__s.slots[0]`);
|
|
23386
|
-
//
|
|
24785
|
+
// native styles fill the following indices during binding mount. Each remaining
|
|
24786
|
+
// control-flow / component / child construct gets the next index. The runtime
|
|
23387
24787
|
// runs the slot calls in `afterCalls` SORTED by source id, so we assign indices in
|
|
23388
24788
|
// that same id order — the scope's `slots` array is then written 0,1,2,… and stays
|
|
23389
24789
|
// PACKED (a holey array, written out of order, would be a slower elements-kind).
|
|
@@ -23398,7 +24798,7 @@ function planJsx(
|
|
|
23398
24798
|
allConstructs.sort((a, b) => a.id - b.id);
|
|
23399
24799
|
// Slot 0 is the binding bag for template bodies; control-flow-only (noTemplate)
|
|
23400
24800
|
// bodies have no bag, so their constructs start at slot 0.
|
|
23401
|
-
const slotBase = noTemplate ? 0 : 1;
|
|
24801
|
+
const slotBase = noTemplate ? 0 : 1 + nativeStyleSlots;
|
|
23402
24802
|
for (let i = 0; i < allConstructs.length; i++) allConstructs[i].slotIndex = i + slotBase;
|
|
23403
24803
|
// Hoisted head elements take the slots AFTER the constructs (and `plan.head` runs
|
|
23404
24804
|
// after `plan.after`), so the scope's `slots` array fills 0,1,…,N,N+1,… packed.
|
|
@@ -23743,26 +25143,37 @@ function planJsx(
|
|
|
23743
25143
|
ctx.runtimeNeeded.add('ifBlock');
|
|
23744
25144
|
registerDirectiveOrigin(ctx, org, ['_$ifBlock', ic.thenHelper, ic.elseHelper]);
|
|
23745
25145
|
registerClauseOrigin(ctx, ic.alternateKeyword, [ic.elseHelper]);
|
|
23746
|
-
|
|
23747
|
-
|
|
23748
|
-
|
|
23749
|
-
|
|
23750
|
-
|
|
23751
|
-
|
|
23752
|
-
|
|
23753
|
-
|
|
23754
|
-
|
|
23755
|
-
...trailing,
|
|
23756
|
-
),
|
|
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,
|
|
23757
25155
|
);
|
|
23758
|
-
|
|
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);
|
|
25166
|
+
pushAfterStmt(ic.id, org, conditionDeclaration ? b.block([conditionDeclaration, call]) : call);
|
|
23759
25167
|
}
|
|
23760
25168
|
for (const cc of compCalls) {
|
|
23761
25169
|
const slotIndex = cc.slotIndex;
|
|
23762
25170
|
const org = cc.origin ?? planOrigin;
|
|
23763
25171
|
const hostExpr = () => hostNodeFor(`_compHost$${cc.id}`);
|
|
23764
25172
|
if (cc.hostChildrenBinding != null) {
|
|
23765
|
-
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
|
+
};
|
|
23766
25177
|
cc.valueExpr = b.conditional(
|
|
23767
25178
|
propertyIsEnumerableCall(props(), 'children'),
|
|
23768
25179
|
b.member(props(), 'children'),
|
|
@@ -23777,6 +25188,59 @@ function planJsx(
|
|
|
23777
25188
|
// component calls; only the emitted runtime call differs.
|
|
23778
25189
|
if (cc.isChild) {
|
|
23779
25190
|
const V = () => b.id('_v');
|
|
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
|
+
}
|
|
23780
25244
|
// MARKERLESS only-child renderable: append a primitive as a single Text
|
|
23781
25245
|
// node (no `<!>`, no slot state). Either the shared helper or the inline
|
|
23782
25246
|
// row guard compares its cached value/node before the ordinary child path.
|
|
@@ -23883,7 +25347,6 @@ function planJsx(
|
|
|
23883
25347
|
}
|
|
23884
25348
|
// Anchor node (null = append into an in-template element host) — see
|
|
23885
25349
|
// anchorNodeFor.
|
|
23886
|
-
const childAnchor = anchorNodeFor(cc, 'compAnchor');
|
|
23887
25350
|
if (noTemplate) {
|
|
23888
25351
|
// No bag to cache on → the small `textSlot` wrapper (fast inline for a
|
|
23889
25352
|
// primitive into a text slot; delegates to `childSlot` otherwise).
|
|
@@ -23968,17 +25431,36 @@ function planJsx(
|
|
|
23968
25431
|
liteMemo ? 'componentSlotLite' : cc.voidComponent ? 'componentSlotVoid' : 'componentSlot',
|
|
23969
25432
|
);
|
|
23970
25433
|
const memoAnchor = anchorNodeFor(cc, 'compAnchor');
|
|
23971
|
-
const trailing =
|
|
23972
|
-
|
|
23973
|
-
|
|
23974
|
-
|
|
23975
|
-
|
|
23976
|
-
|
|
23977
|
-
|
|
23978
|
-
|
|
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
|
+
];
|
|
23979
25444
|
const witnessMiss = cc.autoMemoWitnesses.length
|
|
23980
25445
|
? witnessMissChain(cc.autoMemoWitnesses)
|
|
23981
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
|
+
);
|
|
23982
25464
|
pushAfterStmt(
|
|
23983
25465
|
cc.id,
|
|
23984
25466
|
org,
|
|
@@ -23986,17 +25468,7 @@ function planJsx(
|
|
|
23986
25468
|
ctx,
|
|
23987
25469
|
cc.autoMemoDeps,
|
|
23988
25470
|
slotIndex,
|
|
23989
|
-
b.stmt(
|
|
23990
|
-
b.call(
|
|
23991
|
-
componentHelper,
|
|
23992
|
-
b.id('__s'),
|
|
23993
|
-
b.literal(slotIndex),
|
|
23994
|
-
hostExpr(),
|
|
23995
|
-
cc.compNode,
|
|
23996
|
-
cc.propsExpr,
|
|
23997
|
-
...trailing,
|
|
23998
|
-
),
|
|
23999
|
-
),
|
|
25471
|
+
b.stmt(invocation),
|
|
24000
25472
|
witnessMiss,
|
|
24001
25473
|
cc.autoMemoContextAware,
|
|
24002
25474
|
depNodeFor(cc),
|
|
@@ -24036,6 +25508,8 @@ function planJsx(
|
|
|
24036
25508
|
undefinedNode(),
|
|
24037
25509
|
undefinedNode(),
|
|
24038
25510
|
b.literal(true),
|
|
25511
|
+
undefinedNode(),
|
|
25512
|
+
b.literal(cc.invocationSite),
|
|
24039
25513
|
),
|
|
24040
25514
|
),
|
|
24041
25515
|
);
|
|
@@ -24062,7 +25536,8 @@ function planJsx(
|
|
|
24062
25536
|
hostExpr(),
|
|
24063
25537
|
cc.compNode,
|
|
24064
25538
|
cc.propsExpr,
|
|
24065
|
-
|
|
25539
|
+
liteAnchor ?? undefinedNode(),
|
|
25540
|
+
b.literal(cc.invocationSite),
|
|
24066
25541
|
),
|
|
24067
25542
|
),
|
|
24068
25543
|
);
|
|
@@ -24075,52 +25550,32 @@ function planJsx(
|
|
|
24075
25550
|
// unmount move the slot DOM along with the block; an element host with
|
|
24076
25551
|
// no in-template anchor can safely append).
|
|
24077
25552
|
const compAnchor = anchorNodeFor(cc, 'compAnchor');
|
|
24078
|
-
const trailing = [
|
|
24079
|
-
|
|
24080
|
-
|
|
24081
|
-
|
|
24082
|
-
|
|
24083
|
-
|
|
24084
|
-
|
|
24085
|
-
|
|
24086
|
-
|
|
24087
|
-
|
|
24088
|
-
|
|
24089
|
-
|
|
24090
|
-
|
|
24091
|
-
|
|
24092
|
-
|
|
24093
|
-
trailing
|
|
24094
|
-
}
|
|
24095
|
-
// singleRoot is the 8th positional arg (after anchor, key). It's gated on
|
|
24096
|
-
// no-key, so backfill anchor + key placeholders to land it in the right
|
|
24097
|
-
// slot. `true` = proven same-module single-element root; `2` = the
|
|
24098
|
-
// cross-module sentinel (runtime checks the callee's $$singleRoot stamp).
|
|
24099
|
-
if (cc.singleRoot || cc.maybeSingleRoot) {
|
|
24100
|
-
if (!anchorFilled) {
|
|
24101
|
-
trailing.push(undefinedNode());
|
|
24102
|
-
anchorFilled = true;
|
|
24103
|
-
}
|
|
24104
|
-
trailing.push(undefinedNode(), cc.singleRoot ? b.literal(true) : b.literal(2));
|
|
24105
|
-
}
|
|
24106
|
-
// Persist key ownership separately from the current key VALUE so
|
|
24107
|
-
// `key={undefined}` remains an independent reconciliation boundary.
|
|
24108
|
-
if (cc.keyExpr != null) trailing.push(undefinedNode(), undefinedNode(), b.literal(true));
|
|
24109
|
-
pushAfterStmt(
|
|
24110
|
-
cc.id,
|
|
24111
|
-
org,
|
|
24112
|
-
b.stmt(
|
|
24113
|
-
b.call(
|
|
24114
|
-
componentHelper,
|
|
24115
|
-
b.id('__s'),
|
|
24116
|
-
b.literal(slotIndex),
|
|
24117
|
-
hostExpr(),
|
|
24118
|
-
cc.compNode,
|
|
24119
|
-
cc.propsExpr,
|
|
24120
|
-
...trailing,
|
|
24121
|
-
),
|
|
24122
|
-
),
|
|
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,
|
|
24123
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));
|
|
24124
25579
|
}
|
|
24125
25580
|
for (const pc of ctx._portalCalls) {
|
|
24126
25581
|
const slotIndex = pc.slotIndex;
|
|
@@ -24497,6 +25952,70 @@ function commitSourceRows(sources, valueOf) {
|
|
|
24497
25952
|
return b.array(rows);
|
|
24498
25953
|
}
|
|
24499
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
|
+
|
|
25965
|
+
// Recombine a native spread snapshot only for mount/hydration or a key collision.
|
|
25966
|
+
// Spreading the snapshot cannot replay getters: the authored spreads already
|
|
25967
|
+
// copied their values before any of the trailing expressions were evaluated.
|
|
25968
|
+
function styleSpreadObject(bind, spread, valueOf) {
|
|
25969
|
+
return b.object([
|
|
25970
|
+
b.spread(spread),
|
|
25971
|
+
...bind.properties.map((property, i) =>
|
|
25972
|
+
b.prop(
|
|
25973
|
+
'init',
|
|
25974
|
+
inheritOriginLoc(b.literal(property.name), property.key),
|
|
25975
|
+
valueOf(property, i),
|
|
25976
|
+
),
|
|
25977
|
+
),
|
|
25978
|
+
]);
|
|
25979
|
+
}
|
|
25980
|
+
|
|
25981
|
+
function styleSpreadNeedsFullStyle(bind, spread) {
|
|
25982
|
+
return orChain([
|
|
25983
|
+
...bind.properties.map((property) => b.binary('in', b.literal(property.name), spread)),
|
|
25984
|
+
b.unary('!', b.call('_$canSplitStyleProperties')),
|
|
25985
|
+
]);
|
|
25986
|
+
}
|
|
25987
|
+
|
|
25988
|
+
// Native spreads create writable own data properties. Complete that fresh
|
|
25989
|
+
// snapshot in place only when it will be retained as the full style, and no
|
|
25990
|
+
// inherited setter or read-only property could intercept a trailing assignment.
|
|
25991
|
+
function completeStyleSpread(bind, spread, valueOf, needsFull) {
|
|
25992
|
+
return b.conditional(
|
|
25993
|
+
b.logical(
|
|
25994
|
+
'&&',
|
|
25995
|
+
needsFull,
|
|
25996
|
+
b.unary(
|
|
25997
|
+
'!',
|
|
25998
|
+
orChain(
|
|
25999
|
+
bind.properties.map((property) =>
|
|
26000
|
+
b.binary('in', b.literal(property.name), b.id('_$styleObjectPrototype')),
|
|
26001
|
+
),
|
|
26002
|
+
),
|
|
26003
|
+
),
|
|
26004
|
+
),
|
|
26005
|
+
b.sequence([
|
|
26006
|
+
...bind.properties.map((property, i) =>
|
|
26007
|
+
b.assignment(
|
|
26008
|
+
'=',
|
|
26009
|
+
b.member(spread, inheritOriginLoc(b.literal(property.name), property.key), true),
|
|
26010
|
+
valueOf(property, i),
|
|
26011
|
+
),
|
|
26012
|
+
),
|
|
26013
|
+
spread,
|
|
26014
|
+
]),
|
|
26015
|
+
styleSpreadObject(bind, spread, valueOf),
|
|
26016
|
+
);
|
|
26017
|
+
}
|
|
26018
|
+
|
|
24500
26019
|
// Mount for a DEFERRED property-write binding: store the element ref + seed the
|
|
24501
26020
|
// diff field to `undefined`. The every-render diff then performs the actual
|
|
24502
26021
|
// write — including on the first render, since the `undefined` seed makes its
|
|
@@ -24504,15 +26023,18 @@ function commitSourceRows(sources, valueOf) {
|
|
|
24504
26023
|
// `setClassName(el, undefined)` no-op on a freshly-cloned element (so the output
|
|
24505
26024
|
// is byte-identical to the old unconditional mount write).
|
|
24506
26025
|
function emitDeferredMount(bind, elVar, bag) {
|
|
24507
|
-
// Whole-object styles diff on `_sty`; grouped styles
|
|
24508
|
-
// first-render marker. Each
|
|
26026
|
+
// Whole-object and spread-prefix styles diff on `_sty`; other grouped styles
|
|
26027
|
+
// use it only as a first-render marker. Each trailing property keeps a scalar.
|
|
24509
26028
|
bag.constField(
|
|
24510
|
-
bind.
|
|
24511
|
-
? `
|
|
24512
|
-
:
|
|
26029
|
+
bind.signalDirect
|
|
26030
|
+
? `_sig$${bind.id}`
|
|
26031
|
+
: bind.kind === 'style' || bind.kind === 'styleProperties'
|
|
26032
|
+
? `_sty$${bind.id}`
|
|
26033
|
+
: `_prev$${bind.id}`,
|
|
24513
26034
|
bind.kind === 'styleProperty' || bind.kind === 'styleProperties' ? 'style-unset' : 'undefined',
|
|
24514
26035
|
);
|
|
24515
26036
|
if (bind.kind === 'styleProperties') {
|
|
26037
|
+
if (bind.spread) bag.constField(`_styFull$${bind.id}`, 'undefined');
|
|
24516
26038
|
for (let i = 0; i < bind.properties.length; i++) {
|
|
24517
26039
|
bag.constField(`_prev$${bind.id}_${i}`, 'undefined');
|
|
24518
26040
|
}
|
|
@@ -24532,7 +26054,57 @@ function bindingOrigin(bind) {
|
|
|
24532
26054
|
return bind.expr && bind.expr.loc ? bind.expr : bind.origin;
|
|
24533
26055
|
}
|
|
24534
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
|
+
|
|
24535
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 };
|
|
24536
26108
|
if (bind.deferred) return emitDeferredMount(bind, elVar, bag);
|
|
24537
26109
|
const org = bindingOrigin(bind);
|
|
24538
26110
|
const st = (node) => inheritOriginLoc(node, org);
|
|
@@ -24546,6 +26118,46 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
24546
26118
|
// name; everything else in the call maps to the value expression.
|
|
24547
26119
|
const callee = () => attrLoweringToken(b.id(`_$${attrBindingHelper(bind)}`), bind);
|
|
24548
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
|
+
}
|
|
24549
26161
|
// `suppressHydrationWarning`: stamp a JS flag (NOT a DOM attribute) the runtime reads to
|
|
24550
26162
|
// keep the server value + skip the warning on a hydration mismatch for this element.
|
|
24551
26163
|
if (bind.kind === 'suppress') {
|
|
@@ -24571,11 +26183,42 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
24571
26183
|
if (bind.kind === 'formDiagnostic') {
|
|
24572
26184
|
const args = [el(), b.literal(bind.diagnosticKind)];
|
|
24573
26185
|
if (bind.selectedBinding !== undefined) {
|
|
24574
|
-
|
|
26186
|
+
const selected = bind.selectedBinding;
|
|
26187
|
+
args.push(
|
|
26188
|
+
b.arrow(
|
|
26189
|
+
[],
|
|
26190
|
+
bagFieldNode(bag, `${selected.signalDirect ? '_sig' : '_prev'}$${selected.id}`),
|
|
26191
|
+
),
|
|
26192
|
+
);
|
|
24575
26193
|
}
|
|
24576
26194
|
return st(b.stmt(b.call('_$queueFormAuthoringDiagnostic', ...args)));
|
|
24577
26195
|
}
|
|
24578
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
|
+
}
|
|
26214
|
+
case 'nativeStyle': {
|
|
26215
|
+
return st(
|
|
26216
|
+
b.block([
|
|
26217
|
+
...mountHost(),
|
|
26218
|
+
b.stmt(b.call(bind.helper, b.id('__s'), b.literal(bind.slotIndex), el(), bind.expr)),
|
|
26219
|
+
]),
|
|
26220
|
+
);
|
|
26221
|
+
}
|
|
24579
26222
|
case 'textOnlyChild': {
|
|
24580
26223
|
// Only compiler-admitted native placeholders may be reused. An old
|
|
24581
26224
|
// two-argument caller or an excluded custom/template host must retain
|
|
@@ -24649,6 +26292,29 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
24649
26292
|
const sources = commitSourceRows(bind.sources, (binding, spread) =>
|
|
24650
26293
|
b.id(bag.local(`${spread ? '_sp' : '_prev'}$${binding.id}`)),
|
|
24651
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
|
+
}
|
|
24652
26318
|
// The cleanup closure reads the bag through the captured `_b` — the bag
|
|
24653
26319
|
// exists by the time any cleanup runs (committed at mount end), and the
|
|
24654
26320
|
// `_host$` field is re-written by updates, so the read must be live.
|
|
@@ -24674,6 +26340,7 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
24674
26340
|
undefinedNode(),
|
|
24675
26341
|
b.id('__s'),
|
|
24676
26342
|
b.literal(bind.hasNestedChildren === true),
|
|
26343
|
+
...(bind.readStyle ? [b.id(bind.readStyle)] : []),
|
|
24677
26344
|
),
|
|
24678
26345
|
),
|
|
24679
26346
|
),
|
|
@@ -24692,7 +26359,18 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
24692
26359
|
return st(
|
|
24693
26360
|
b.block([
|
|
24694
26361
|
b.const('_v', bind.expr),
|
|
24695
|
-
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
|
+
),
|
|
24696
26374
|
b.stmt(b.assignment('=', local(`_prev$${bind.id}`), V())),
|
|
24697
26375
|
]),
|
|
24698
26376
|
);
|
|
@@ -24754,6 +26432,34 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
24754
26432
|
}
|
|
24755
26433
|
case 'styleProperties': {
|
|
24756
26434
|
for (let i = 0; i < bind.properties.length; i++) bag.local(`_prev$${bind.id}_${i}`);
|
|
26435
|
+
if (bind.spread) {
|
|
26436
|
+
const spreadValues = () => b.id(bind.spreadName);
|
|
26437
|
+
const spread = () => b.member(spreadValues(), b.literal(0), true);
|
|
26438
|
+
const full = () => b.id(bind.fullName);
|
|
26439
|
+
const merged = () => b.id(bind.mergedName);
|
|
26440
|
+
const valueOf = (property) =>
|
|
26441
|
+
b.member(spreadValues(), b.literal(property.evaluationIndex + 1), true);
|
|
26442
|
+
return st(
|
|
26443
|
+
b.block([
|
|
26444
|
+
b.const(bind.spreadName, bind.expr),
|
|
26445
|
+
b.const(bind.fullName, styleSpreadNeedsFullStyle(bind, spread())),
|
|
26446
|
+
b.const(bind.mergedName, completeStyleSpread(bind, spread(), valueOf, full())),
|
|
26447
|
+
b.stmt(b.call(callee(), el(), merged(), undefinedNode())),
|
|
26448
|
+
...mountHost(),
|
|
26449
|
+
b.stmt(
|
|
26450
|
+
b.assignment(
|
|
26451
|
+
'=',
|
|
26452
|
+
local(`_sty$${bind.id}`),
|
|
26453
|
+
b.conditional(full(), merged(), spread()),
|
|
26454
|
+
),
|
|
26455
|
+
),
|
|
26456
|
+
b.stmt(b.assignment('=', local(`_styFull$${bind.id}`), full())),
|
|
26457
|
+
...bind.properties.map((property, i) =>
|
|
26458
|
+
b.stmt(b.assignment('=', local(`_prev$${bind.id}_${i}`), valueOf(property))),
|
|
26459
|
+
),
|
|
26460
|
+
]),
|
|
26461
|
+
);
|
|
26462
|
+
}
|
|
24757
26463
|
const entries =
|
|
24758
26464
|
bind.properties.length === bind.evaluations.length
|
|
24759
26465
|
? V()
|
|
@@ -24920,6 +26626,10 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
24920
26626
|
}
|
|
24921
26627
|
|
|
24922
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 };
|
|
24923
26633
|
if (bind.mountOnly) return null;
|
|
24924
26634
|
const org = bindingOrigin(bind);
|
|
24925
26635
|
const st = (node) => inheritOriginLoc(node, org);
|
|
@@ -24932,7 +26642,79 @@ function emitBindingUpdate(bind, bag, inlineBindingGuards = false) {
|
|
|
24932
26642
|
const callee = () =>
|
|
24933
26643
|
attrLoweringToken(b.id(`_$${attrBindingUpdateHelper(bind, inlineBindingGuards)}`), bind);
|
|
24934
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
|
+
}
|
|
24935
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
|
+
}
|
|
26713
|
+
case 'nativeStyle': {
|
|
26714
|
+
return st(
|
|
26715
|
+
b.stmt(b.call(bind.helper, b.id('__s'), b.literal(bind.slotIndex), F('_el'), bind.expr)),
|
|
26716
|
+
);
|
|
26717
|
+
}
|
|
24936
26718
|
case 'nativeChangeRuntime': {
|
|
24937
26719
|
return st(b.stmt(b.call('_$queueNativeChangeDiagnostic', F('_el'))));
|
|
24938
26720
|
}
|
|
@@ -25002,6 +26784,26 @@ function emitBindingUpdate(bind, bag, inlineBindingGuards = false) {
|
|
|
25002
26784
|
const sources = commitSourceRows(bind.sources, (binding, spread) =>
|
|
25003
26785
|
bagFieldNode(bag, `${spread ? '_sp' : '_prev'}$${binding.id}`),
|
|
25004
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
|
+
}
|
|
25005
26807
|
return st(
|
|
25006
26808
|
b.stmt(
|
|
25007
26809
|
b.assignment(
|
|
@@ -25014,6 +26816,7 @@ function emitBindingUpdate(bind, bag, inlineBindingGuards = false) {
|
|
|
25014
26816
|
F('_host'),
|
|
25015
26817
|
b.id('__s'),
|
|
25016
26818
|
b.literal(bind.hasNestedChildren === true),
|
|
26819
|
+
...(bind.readStyle ? [b.id(bind.readStyle)] : []),
|
|
25017
26820
|
),
|
|
25018
26821
|
),
|
|
25019
26822
|
),
|
|
@@ -25131,8 +26934,76 @@ function emitBindingUpdate(bind, bag, inlineBindingGuards = false) {
|
|
|
25131
26934
|
// Evaluate every authored value before the first CSS write, including
|
|
25132
26935
|
// fixed literal suffix values. Mount and hydration compare one complete
|
|
25133
26936
|
// style; ordinary updates only call the scalar setter for changed cells.
|
|
25134
|
-
const
|
|
25135
|
-
const
|
|
26937
|
+
const valueName = (i) => bind.valueNames?.[i] ?? `_v${i}`;
|
|
26938
|
+
const values = bind.evaluations.map((value, i) => b.const(valueName(i), value));
|
|
26939
|
+
const valueOf = (property) => b.id(valueName(property.evaluationIndex));
|
|
26940
|
+
if (bind.spread) {
|
|
26941
|
+
const spread = b.id(bind.spreadName);
|
|
26942
|
+
const needsFull = b.id(bind.fullName);
|
|
26943
|
+
const merged = b.id(bind.mergedName);
|
|
26944
|
+
const previous = (property, i) => bagFieldNode(bag, `_prev$${bind.id}_${i}`);
|
|
26945
|
+
const initial = bind.deferred ? b.binary('===', F('_sty'), b.id('__s')) : b.literal(false);
|
|
26946
|
+
const previousStyle = b.conditional(
|
|
26947
|
+
F('_styFull'),
|
|
26948
|
+
F('_sty'),
|
|
26949
|
+
styleSpreadObject(bind, F('_sty'), previous),
|
|
26950
|
+
);
|
|
26951
|
+
const full = b.stmt(
|
|
26952
|
+
b.call(
|
|
26953
|
+
callee(),
|
|
26954
|
+
F('_el'),
|
|
26955
|
+
merged,
|
|
26956
|
+
bind.deferred ? b.conditional(initial, undefinedNode(), previousStyle) : previousStyle,
|
|
26957
|
+
),
|
|
26958
|
+
);
|
|
26959
|
+
const scalar = bind.properties.map((property, i) =>
|
|
26960
|
+
b.if(
|
|
26961
|
+
b.binary('!==', previous(property, i), valueOf(property)),
|
|
26962
|
+
b.block([
|
|
26963
|
+
b.stmt(
|
|
26964
|
+
b.call(
|
|
26965
|
+
attrLoweringToken(b.id('_$setStyleProperty'), bind),
|
|
26966
|
+
F('_el'),
|
|
26967
|
+
inheritOriginLoc(b.literal(property.name), property.key),
|
|
26968
|
+
valueOf(property),
|
|
26969
|
+
b.literal(''),
|
|
26970
|
+
previous(property, i),
|
|
26971
|
+
),
|
|
26972
|
+
),
|
|
26973
|
+
]),
|
|
26974
|
+
null,
|
|
26975
|
+
),
|
|
26976
|
+
);
|
|
26977
|
+
// A spread can insert a trailing key before a shorthand. Retain the
|
|
26978
|
+
// complete object when required, so consecutive full diffs reuse it.
|
|
26979
|
+
// This also preserves mutations by inherited getters on the complete
|
|
26980
|
+
// receiver. Leaving full mode needs one complete diff before returning
|
|
26981
|
+
// to prefix/scalar updates.
|
|
26982
|
+
return st(
|
|
26983
|
+
b.block([
|
|
26984
|
+
b.const(bind.spreadName, bind.spread),
|
|
26985
|
+
...values,
|
|
26986
|
+
b.const(bind.fullName, styleSpreadNeedsFullStyle(bind, spread)),
|
|
26987
|
+
b.if(
|
|
26988
|
+
orChain([initial, needsFull, F('_styFull')]),
|
|
26989
|
+
b.block([
|
|
26990
|
+
b.const(bind.mergedName, completeStyleSpread(bind, spread, valueOf, needsFull)),
|
|
26991
|
+
full,
|
|
26992
|
+
b.stmt(b.assignment('=', F('_sty'), b.conditional(needsFull, merged, spread))),
|
|
26993
|
+
]),
|
|
26994
|
+
b.block([
|
|
26995
|
+
b.stmt(b.call(callee(), F('_el'), spread, F('_sty'))),
|
|
26996
|
+
...scalar,
|
|
26997
|
+
b.stmt(b.assignment('=', F('_sty'), spread)),
|
|
26998
|
+
]),
|
|
26999
|
+
),
|
|
27000
|
+
b.stmt(b.assignment('=', F('_styFull'), needsFull)),
|
|
27001
|
+
...bind.properties.map((property, i) =>
|
|
27002
|
+
b.stmt(b.assignment('=', previous(property, i), valueOf(property))),
|
|
27003
|
+
),
|
|
27004
|
+
]),
|
|
27005
|
+
);
|
|
27006
|
+
}
|
|
25136
27007
|
const entries = b.array(
|
|
25137
27008
|
bind.properties.flatMap((property) => [
|
|
25138
27009
|
inheritOriginLoc(b.literal(property.name), property.key),
|
|
@@ -25275,233 +27146,35 @@ function emitBindingUpdate(bind, bag, inlineBindingGuards = false) {
|
|
|
25275
27146
|
// detaches the new ref.
|
|
25276
27147
|
const fi = () => b.id('_fi');
|
|
25277
27148
|
const cur = () => b.member(fi(), '_currentRef');
|
|
25278
|
-
return st(
|
|
25279
|
-
b.block([
|
|
25280
|
-
b.const('_r', bind.expr),
|
|
25281
|
-
b.const('_fi', F('_fi')),
|
|
25282
|
-
b.if(
|
|
25283
|
-
andChain([fi(), b.binary('!==', b.id('_r'), cur())]),
|
|
25284
|
-
b.stmt(
|
|
25285
|
-
b.assignment(
|
|
25286
|
-
'=',
|
|
25287
|
-
cur(),
|
|
25288
|
-
b.call('_$replaceRef', b.id('__s'), cur(), b.id('_r'), fi()),
|
|
25289
|
-
),
|
|
25290
|
-
),
|
|
25291
|
-
null,
|
|
25292
|
-
),
|
|
25293
|
-
]),
|
|
25294
|
-
);
|
|
25295
|
-
}
|
|
25296
|
-
}
|
|
25297
|
-
return null;
|
|
25298
|
-
}
|
|
25299
|
-
|
|
25300
|
-
// ===========================================================================
|
|
25301
|
-
// HTML emission
|
|
25302
|
-
// ===========================================================================
|
|
25303
|
-
|
|
25304
|
-
// ---------------------------------------------------------------------------
|
|
25305
|
-
// Template IR + origin recording (`inspect: true`). Static template HTML is
|
|
25306
|
-
// runtime DATA, not JavaScript syntax: the browser-facing `template(html)` ABI
|
|
25307
|
-
// intentionally stays a string. The compiler represents that data as an
|
|
25308
|
-
// structured tree of elements and typed data chunks until allocTemplate
|
|
25309
|
-
// serializes the completed template exactly once. Child templates compose by
|
|
25310
|
-
// transferring nodes, so no intermediate HTML is joined and no origin offsets
|
|
25311
|
-
// need to be shifted during construction. Origin spans remain local to their
|
|
25312
|
-
// chunk; the one serializer shifts them into final logical-HTML coordinates.
|
|
25313
|
-
//
|
|
25314
|
-
// Origin collection stays gated on `ctx.inspect`. The normal compile path pays
|
|
25315
|
-
// for the template chunks (the structural representation replacing string
|
|
25316
|
-
// concatenation) but not for source-span arrays.
|
|
25317
|
-
// ---------------------------------------------------------------------------
|
|
25318
|
-
|
|
25319
|
-
/**
|
|
25320
|
-
* @typedef {{ start: number, end: number, srcStart: number, srcEnd: number,
|
|
25321
|
-
* kind: 'tag-open' | 'tag-close' | 'attr-name' | 'attr-value' | 'text' }} TemplateOrigin
|
|
25322
|
-
*/
|
|
25323
|
-
|
|
25324
|
-
/**
|
|
25325
|
-
* @typedef {'syntax' | 'tag-open' | 'tag-close' | 'attribute' | 'text' |
|
|
25326
|
-
* 'anchor' | 'fragment-open' | 'fragment-close' | 'raw'} TemplatePartKind
|
|
25327
|
-
*/
|
|
25328
|
-
|
|
25329
|
-
/**
|
|
25330
|
-
* @typedef {{
|
|
25331
|
-
* type: 'TemplatePart',
|
|
25332
|
-
* kind: TemplatePartKind,
|
|
25333
|
-
* value: string,
|
|
25334
|
-
* origins: TemplateOrigin[] | null,
|
|
25335
|
-
* length: number,
|
|
25336
|
-
* }} TemplatePart
|
|
25337
|
-
*/
|
|
25338
|
-
|
|
25339
|
-
/**
|
|
25340
|
-
* @typedef {{
|
|
25341
|
-
* type: 'TemplateElement',
|
|
25342
|
-
* tag: string,
|
|
25343
|
-
* namespace: string,
|
|
25344
|
-
* synthetic: boolean,
|
|
25345
|
-
* opening: TemplatePart,
|
|
25346
|
-
* attributes: TemplatePart[],
|
|
25347
|
-
* openingEnd: TemplatePart,
|
|
25348
|
-
* children: TemplateNode[],
|
|
25349
|
-
* closing: TemplatePart | null,
|
|
25350
|
-
* length: number,
|
|
25351
|
-
* }} TemplateElement
|
|
25352
|
-
*/
|
|
25353
|
-
|
|
25354
|
-
/** @typedef {TemplatePart | TemplateElement} TemplateNode */
|
|
25355
|
-
|
|
25356
|
-
/**
|
|
25357
|
-
* @typedef {{
|
|
25358
|
-
* type: 'Template',
|
|
25359
|
-
* parts: TemplateNode[],
|
|
25360
|
-
* length: number,
|
|
25361
|
-
* }} TemplateIR
|
|
25362
|
-
*/
|
|
25363
|
-
|
|
25364
|
-
/** @returns {TemplateIR} */
|
|
25365
|
-
function createTemplateIr() {
|
|
25366
|
-
return { type: 'Template', parts: [], length: 0 };
|
|
25367
|
-
}
|
|
25368
|
-
|
|
25369
|
-
/**
|
|
25370
|
-
* @param {string} value
|
|
25371
|
-
* @param {TemplatePartKind} [kind]
|
|
25372
|
-
* @param {TemplateOrigin[] | null} [origins]
|
|
25373
|
-
* @returns {TemplatePart}
|
|
25374
|
-
*/
|
|
25375
|
-
function createTemplatePart(value, kind = 'raw', origins = null) {
|
|
25376
|
-
return { type: 'TemplatePart', kind, value, origins, length: value.length };
|
|
25377
|
-
}
|
|
25378
|
-
|
|
25379
|
-
/** @param {TemplateIR} template @param {TemplateNode} node */
|
|
25380
|
-
function appendTemplateNode(template, node) {
|
|
25381
|
-
template.parts.push(node);
|
|
25382
|
-
template.length += node.length;
|
|
25383
|
-
}
|
|
25384
|
-
|
|
25385
|
-
/**
|
|
25386
|
-
* @param {TemplateIR} template
|
|
25387
|
-
* @param {string} value
|
|
25388
|
-
* @param {TemplatePartKind} [kind]
|
|
25389
|
-
* @param {TemplateOrigin[] | null} [origins]
|
|
25390
|
-
*/
|
|
25391
|
-
function appendTemplatePart(template, value, kind = 'raw', origins = null) {
|
|
25392
|
-
if (value === '') return;
|
|
25393
|
-
appendTemplateNode(template, createTemplatePart(value, kind, origins));
|
|
25394
|
-
}
|
|
25395
|
-
|
|
25396
|
-
/** @param {TemplateIR} into @param {TemplateIR} child */
|
|
25397
|
-
function appendTemplateIr(into, child) {
|
|
25398
|
-
if (child.length === 0) return;
|
|
25399
|
-
for (const part of child.parts) into.parts.push(part);
|
|
25400
|
-
into.length += child.length;
|
|
25401
|
-
}
|
|
25402
|
-
|
|
25403
|
-
/**
|
|
25404
|
-
* @param {string} value
|
|
25405
|
-
* @param {TemplatePartKind} [kind]
|
|
25406
|
-
* @param {TemplateOrigin[] | null} [origins]
|
|
25407
|
-
* @returns {TemplateIR}
|
|
25408
|
-
*/
|
|
25409
|
-
function templatePart(value, kind = 'raw', origins = null) {
|
|
25410
|
-
const template = createTemplateIr();
|
|
25411
|
-
appendTemplatePart(template, value, kind, origins);
|
|
25412
|
-
return template;
|
|
25413
|
-
}
|
|
25414
|
-
|
|
25415
|
-
/**
|
|
25416
|
-
* @param {string} tag
|
|
25417
|
-
* @param {string} namespace
|
|
25418
|
-
* @param {boolean} voidElement
|
|
25419
|
-
* @param {TemplateIR} attributes
|
|
25420
|
-
* @param {TemplateIR} children
|
|
25421
|
-
* @param {TemplateOrigin[] | null} openOrigins
|
|
25422
|
-
* @param {TemplateOrigin[] | null} closeOrigins
|
|
25423
|
-
* @param {boolean} [synthetic]
|
|
25424
|
-
* @returns {TemplateIR}
|
|
25425
|
-
*/
|
|
25426
|
-
function templateElement(
|
|
25427
|
-
tag,
|
|
25428
|
-
namespace,
|
|
25429
|
-
voidElement,
|
|
25430
|
-
attributes,
|
|
25431
|
-
children,
|
|
25432
|
-
openOrigins,
|
|
25433
|
-
closeOrigins,
|
|
25434
|
-
synthetic = false,
|
|
25435
|
-
) {
|
|
25436
|
-
const opening = createTemplatePart(`<${tag}`, 'tag-open', openOrigins);
|
|
25437
|
-
const openingEnd = createTemplatePart(voidElement ? '/>' : '>', 'syntax');
|
|
25438
|
-
const closing = voidElement ? null : createTemplatePart(`</${tag}>`, 'tag-close', closeOrigins);
|
|
25439
|
-
const node = {
|
|
25440
|
-
type: 'TemplateElement',
|
|
25441
|
-
tag,
|
|
25442
|
-
namespace,
|
|
25443
|
-
synthetic,
|
|
25444
|
-
opening,
|
|
25445
|
-
attributes: /** @type {TemplatePart[]} */ (attributes.parts),
|
|
25446
|
-
openingEnd,
|
|
25447
|
-
children: children.parts,
|
|
25448
|
-
closing,
|
|
25449
|
-
length:
|
|
25450
|
-
opening.value.length +
|
|
25451
|
-
attributes.length +
|
|
25452
|
-
openingEnd.value.length +
|
|
25453
|
-
children.length +
|
|
25454
|
-
(closing === null ? 0 : closing.value.length),
|
|
25455
|
-
};
|
|
25456
|
-
const template = createTemplateIr();
|
|
25457
|
-
appendTemplateNode(template, node);
|
|
25458
|
-
return template;
|
|
25459
|
-
}
|
|
25460
|
-
|
|
25461
|
-
/**
|
|
25462
|
-
* Serialize the completed template once. The IR already contains the exact
|
|
25463
|
-
* runtime bytes; this pass only joins them and converts chunk-local origins to
|
|
25464
|
-
* final logical-HTML offsets.
|
|
25465
|
-
*
|
|
25466
|
-
* @param {TemplateIR} template
|
|
25467
|
-
*/
|
|
25468
|
-
function serializeTemplateIr(template) {
|
|
25469
|
-
const values = [];
|
|
25470
|
-
let origins = null;
|
|
25471
|
-
let offset = 0;
|
|
25472
|
-
const appendPart = (part) => {
|
|
25473
|
-
values.push(part.value);
|
|
25474
|
-
if (part.origins !== null) {
|
|
25475
|
-
origins ??= [];
|
|
25476
|
-
for (const origin of part.origins) {
|
|
25477
|
-
origins.push({
|
|
25478
|
-
start: origin.start + offset,
|
|
25479
|
-
end: origin.end + offset,
|
|
25480
|
-
srcStart: origin.srcStart,
|
|
25481
|
-
srcEnd: origin.srcEnd,
|
|
25482
|
-
kind: origin.kind,
|
|
25483
|
-
});
|
|
25484
|
-
}
|
|
25485
|
-
}
|
|
25486
|
-
offset += part.value.length;
|
|
25487
|
-
};
|
|
25488
|
-
const visit = (node) => {
|
|
25489
|
-
if (node.type === 'TemplatePart') {
|
|
25490
|
-
appendPart(node);
|
|
25491
|
-
return;
|
|
27149
|
+
return st(
|
|
27150
|
+
b.block([
|
|
27151
|
+
b.const('_r', bind.expr),
|
|
27152
|
+
b.const('_fi', F('_fi')),
|
|
27153
|
+
b.if(
|
|
27154
|
+
andChain([fi(), b.binary('!==', b.id('_r'), cur())]),
|
|
27155
|
+
b.stmt(
|
|
27156
|
+
b.assignment(
|
|
27157
|
+
'=',
|
|
27158
|
+
cur(),
|
|
27159
|
+
b.call('_$replaceRef', b.id('__s'), cur(), b.id('_r'), fi()),
|
|
27160
|
+
),
|
|
27161
|
+
),
|
|
27162
|
+
null,
|
|
27163
|
+
),
|
|
27164
|
+
]),
|
|
27165
|
+
);
|
|
25492
27166
|
}
|
|
25493
|
-
appendPart(node.opening);
|
|
25494
|
-
for (const attribute of node.attributes) appendPart(attribute);
|
|
25495
|
-
appendPart(node.openingEnd);
|
|
25496
|
-
for (const child of node.children) visit(child);
|
|
25497
|
-
if (node.closing !== null) appendPart(node.closing);
|
|
25498
|
-
};
|
|
25499
|
-
for (const node of template.parts) {
|
|
25500
|
-
visit(node);
|
|
25501
27167
|
}
|
|
25502
|
-
return
|
|
27168
|
+
return null;
|
|
25503
27169
|
}
|
|
25504
27170
|
|
|
27171
|
+
// ===========================================================================
|
|
27172
|
+
// HTML emission
|
|
27173
|
+
// ===========================================================================
|
|
27174
|
+
|
|
27175
|
+
/** @typedef {import('./template-ir.js').TemplateIR} TemplateIR */
|
|
27176
|
+
/** @typedef {import('./template-ir.js').TemplateOrigin} TemplateOrigin */
|
|
27177
|
+
|
|
25505
27178
|
// Record the origin entries for one static-attribute chunk appended at `base`
|
|
25506
27179
|
// (a bakeStaticAttr result or the inline ` style="…"` bakes, which share the
|
|
25507
27180
|
// same layout). Uses bakeStaticAttr's documented LAYOUT INVARIANT — ` name`
|
|
@@ -25572,15 +27245,36 @@ function emitNodeHtml(
|
|
|
25572
27245
|
parentNs = 'html',
|
|
25573
27246
|
cssHash = null,
|
|
25574
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
|
+
}
|
|
25575
27256
|
if (node.type === 'Text') {
|
|
25576
|
-
if (
|
|
25577
|
-
|
|
25578
|
-
|
|
25579
|
-
|
|
25580
|
-
|
|
25581
|
-
|
|
25582
|
-
|
|
25583
|
-
|
|
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
|
+
);
|
|
25584
27278
|
return templatePart('<!>', 'anchor');
|
|
25585
27279
|
}
|
|
25586
27280
|
// Bare `{expr}` (no string cast) → RENDERABLE hole at a top-level / multi-
|
|
@@ -25599,6 +27293,28 @@ function emitNodeHtml(
|
|
|
25599
27293
|
// cannot represent a Fragment ref or template-only control flow in that body.
|
|
25600
27294
|
// Ordinary root portals keep their existing descriptor/childSlot lowering.
|
|
25601
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
|
+
}
|
|
25602
27318
|
const fragmentBindings = ctx._fragRefStack;
|
|
25603
27319
|
if (
|
|
25604
27320
|
isCreatePortalCall(node.expression) &&
|
|
@@ -25908,7 +27624,9 @@ function emitElementHtml(
|
|
|
25908
27624
|
const hostNs = nsForSelf(tag, parentNs);
|
|
25909
27625
|
const childNs = nsForChildren(tag, parentNs);
|
|
25910
27626
|
// Collect attributes.
|
|
25911
|
-
const attrs =
|
|
27627
|
+
const attrs = expandKnownAttributeSpreads(
|
|
27628
|
+
node.attributes || node.openingElement?.attributes || [],
|
|
27629
|
+
);
|
|
25912
27630
|
// A null/undefined child alongside direct raw HTML is semantically absent.
|
|
25913
27631
|
// Suppress its child binding so hydration cannot clear the raw HTML that the
|
|
25914
27632
|
// preceding binding just adopted/applied.
|
|
@@ -25924,7 +27642,7 @@ function emitElementHtml(
|
|
|
25924
27642
|
// into the template HTML (the spread would clobber them at runtime) —
|
|
25925
27643
|
// emit them as bindings in source order instead.
|
|
25926
27644
|
const firstSpreadIdx = attrs.findIndex(
|
|
25927
|
-
(a) => a.type === 'SpreadAttribute' || a.type === 'JSXSpreadAttribute',
|
|
27645
|
+
(a) => !a.knownSource && (a.type === 'SpreadAttribute' || a.type === 'JSXSpreadAttribute'),
|
|
25928
27646
|
);
|
|
25929
27647
|
const directPropIdentities = new Set();
|
|
25930
27648
|
let hasDuplicateDirectProp = false;
|
|
@@ -25959,11 +27677,21 @@ function emitElementHtml(
|
|
|
25959
27677
|
(tag === 'select' &&
|
|
25960
27678
|
directPropNames.has('multiple') &&
|
|
25961
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
|
+
});
|
|
25962
27689
|
const resolveHostPropsAcrossSources =
|
|
25963
27690
|
firstSpreadIdx !== -1 ||
|
|
25964
27691
|
hasDuplicateDirectProp ||
|
|
25965
27692
|
hasDirectFormCascade ||
|
|
25966
|
-
resolveDevCustomizedHost
|
|
27693
|
+
resolveDevCustomizedHost ||
|
|
27694
|
+
hasDirectSignalStyle;
|
|
25967
27695
|
const hostClientSources = [];
|
|
25968
27696
|
let directChildrenClientBinding = null;
|
|
25969
27697
|
let hostCommitClientBinding = null;
|
|
@@ -26004,9 +27732,45 @@ function emitElementHtml(
|
|
|
26004
27732
|
typeWriters.length === 1 &&
|
|
26005
27733
|
(staticInputType === 'checkbox' || staticInputType === 'radio');
|
|
26006
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
|
+
}
|
|
26007
27758
|
let sawRef = false;
|
|
26008
27759
|
for (let attrI = 0; attrI < attrs.length; attrI++) {
|
|
26009
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
|
+
}
|
|
26010
27774
|
// `<div {...props}/>` — runtime spread. Emits one setSpread binding that
|
|
26011
27775
|
// routes each key (class / style / on… / attr / ref) and diffs against
|
|
26012
27776
|
// the prior spread object to clear removed keys.
|
|
@@ -26015,6 +27779,7 @@ function emitElementHtml(
|
|
|
26015
27779
|
const binding = {
|
|
26016
27780
|
id: bindings.length,
|
|
26017
27781
|
kind: 'hostSpread',
|
|
27782
|
+
...(attr._octaneBindingRest ? { bindingRest: attr._octaneBindingRest } : {}),
|
|
26018
27783
|
// The INNER spread argument; the emit re-derives the
|
|
26019
27784
|
// `_$snapshotSpread(…)` wrapper around it.
|
|
26020
27785
|
expr: tsrxExprNode(attr.argument, ctx, componentName, inlinedSubs),
|
|
@@ -26027,6 +27792,24 @@ function emitElementHtml(
|
|
|
26027
27792
|
}
|
|
26028
27793
|
if (attr.type !== 'Attribute' && attr.type !== 'JSXAttribute') continue;
|
|
26029
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
|
+
}
|
|
26030
27813
|
if (resolveHostPropsAcrossSources) {
|
|
26031
27814
|
if (rawAttrName === 'ref') {
|
|
26032
27815
|
if (sawRef) {
|
|
@@ -26066,6 +27849,13 @@ function emitElementHtml(
|
|
|
26066
27849
|
ctx.inspect,
|
|
26067
27850
|
);
|
|
26068
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
|
+
}
|
|
26069
27859
|
expr = tsrxExprNode(inner, ctx, componentName, inlinedSubs);
|
|
26070
27860
|
}
|
|
26071
27861
|
const binding = {
|
|
@@ -26257,19 +28047,17 @@ function emitElementHtml(
|
|
|
26257
28047
|
resolveFormControlsAcrossSpreads &&
|
|
26258
28048
|
(ctlKind !== null || (tag === 'select' && attrName === 'multiple'))
|
|
26259
28049
|
) {
|
|
28050
|
+
const sourceExpr =
|
|
28051
|
+
val == null
|
|
28052
|
+
? b.literal(true, 'true', attr)
|
|
28053
|
+
: val.type === 'JSXExpressionContainer'
|
|
28054
|
+
? val.expression
|
|
28055
|
+
: val;
|
|
26260
28056
|
const binding = {
|
|
26261
28057
|
id: bindings.length,
|
|
26262
28058
|
kind: 'formValue',
|
|
26263
28059
|
name: attrName,
|
|
26264
|
-
expr:
|
|
26265
|
-
val == null
|
|
26266
|
-
? b.literal(true, 'true', attr)
|
|
26267
|
-
: tsrxExprNode(
|
|
26268
|
-
val.type === 'JSXExpressionContainer' ? val.expression : val,
|
|
26269
|
-
ctx,
|
|
26270
|
-
componentName,
|
|
26271
|
-
inlinedSubs,
|
|
26272
|
-
),
|
|
28060
|
+
expr: tsrxExprNode(sourceExpr, ctx, componentName, inlinedSubs),
|
|
26273
28061
|
path,
|
|
26274
28062
|
};
|
|
26275
28063
|
bindings.push(binding);
|
|
@@ -26287,22 +28075,28 @@ function emitElementHtml(
|
|
|
26287
28075
|
} else if (ctlKind === 'checked' && leanChecked) {
|
|
26288
28076
|
ctlKind = 'checkedCheckable';
|
|
26289
28077
|
}
|
|
26290
|
-
|
|
28078
|
+
const sourceExpr =
|
|
28079
|
+
val == null
|
|
28080
|
+
? b.literal(true, 'true', attr)
|
|
28081
|
+
: val.type === 'JSXExpressionContainer'
|
|
28082
|
+
? val.expression
|
|
28083
|
+
: val;
|
|
28084
|
+
const binding = {
|
|
26291
28085
|
id: bindings.length,
|
|
26292
28086
|
kind: ctlKind,
|
|
26293
|
-
expr:
|
|
26294
|
-
val == null
|
|
26295
|
-
? b.literal(true, 'true', attr)
|
|
26296
|
-
: tsrxExprNode(
|
|
26297
|
-
val.type === 'JSXExpressionContainer' ? val.expression : val,
|
|
26298
|
-
ctx,
|
|
26299
|
-
componentName,
|
|
26300
|
-
inlinedSubs,
|
|
26301
|
-
),
|
|
28087
|
+
expr: tsrxExprNode(sourceExpr, ctx, componentName, inlinedSubs),
|
|
26302
28088
|
path,
|
|
26303
28089
|
ns: hostNs,
|
|
26304
28090
|
nameOrigin: attr.name,
|
|
26305
|
-
}
|
|
28091
|
+
};
|
|
28092
|
+
bindings.push(
|
|
28093
|
+
ctlKind === 'value' ||
|
|
28094
|
+
ctlKind === 'selectValue' ||
|
|
28095
|
+
ctlKind === 'checked' ||
|
|
28096
|
+
ctlKind === 'checkedCheckable'
|
|
28097
|
+
? markDirectControl(binding, sourceExpr, attr)
|
|
28098
|
+
: binding,
|
|
28099
|
+
);
|
|
26306
28100
|
continue;
|
|
26307
28101
|
}
|
|
26308
28102
|
// `autoFocus` never bakes/writes an attribute — React parity: the
|
|
@@ -26359,11 +28153,32 @@ function emitElementHtml(
|
|
|
26359
28153
|
// template (unless we're after a spread, which would clobber it); a fixed
|
|
26360
28154
|
// literal prefix plus dynamic suffix can avoid a whole-object diff.
|
|
26361
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
|
+
}
|
|
26362
28163
|
if (!isAfterSpread && inner.type === 'Literal' && typeof inner.value === 'string') {
|
|
26363
28164
|
const chunk = ` style="${escapeAttr(inner.value)}"`;
|
|
26364
28165
|
appendBakedAttribute(attrTemplate, chunk, attrName, attr.name, inner, ctx.inspect);
|
|
26365
28166
|
continue;
|
|
26366
28167
|
}
|
|
28168
|
+
if (
|
|
28169
|
+
ctx.nativeReads &&
|
|
28170
|
+
(inner.type !== 'ObjectExpression' || !objectExprIsStaticLiteral(inner))
|
|
28171
|
+
) {
|
|
28172
|
+
bindings.push({
|
|
28173
|
+
id: bindings.length,
|
|
28174
|
+
kind: 'nativeStyle',
|
|
28175
|
+
expr: tsrxExprNode(inner, ctx, componentName, inlinedSubs),
|
|
28176
|
+
path,
|
|
28177
|
+
ns: hostNs,
|
|
28178
|
+
nameOrigin: attr.name,
|
|
28179
|
+
});
|
|
28180
|
+
continue;
|
|
28181
|
+
}
|
|
26367
28182
|
if (
|
|
26368
28183
|
!isAfterSpread &&
|
|
26369
28184
|
inner.type === 'ObjectExpression' &&
|
|
@@ -26398,7 +28213,7 @@ function emitElementHtml(
|
|
|
26398
28213
|
for (const entry of mixed.dynamics) {
|
|
26399
28214
|
registerAttrLoweringOrigin(ctx, entry.key, null, entry.name);
|
|
26400
28215
|
}
|
|
26401
|
-
if (mixed.dynamics.length === 1) {
|
|
28216
|
+
if (mixed.dynamics.length === 1 && mixed.spread === null) {
|
|
26402
28217
|
const entry = mixed.dynamics[0];
|
|
26403
28218
|
bindings.push({
|
|
26404
28219
|
id: bindings.length,
|
|
@@ -26413,6 +28228,9 @@ function emitElementHtml(
|
|
|
26413
28228
|
staticCss: mixed.css,
|
|
26414
28229
|
});
|
|
26415
28230
|
} else {
|
|
28231
|
+
const spread = mixed.spread
|
|
28232
|
+
? tsrxExprNode(mixed.spread, ctx, componentName, inlinedSubs)
|
|
28233
|
+
: null;
|
|
26416
28234
|
const entries = [];
|
|
26417
28235
|
const propertyBindings = new Map();
|
|
26418
28236
|
const evaluations = [];
|
|
@@ -26429,7 +28247,14 @@ function emitElementHtml(
|
|
|
26429
28247
|
bindings.push({
|
|
26430
28248
|
id: bindings.length,
|
|
26431
28249
|
kind: 'styleProperties',
|
|
26432
|
-
expr: inheritOriginLoc(b.array(entries), inner),
|
|
28250
|
+
expr: inheritOriginLoc(b.array(spread ? [spread, ...evaluations] : entries), inner),
|
|
28251
|
+
spread,
|
|
28252
|
+
spreadName: spread ? allocCompilerName(ctx, '__styleSpread') : null,
|
|
28253
|
+
fullName: spread ? allocCompilerName(ctx, '__styleFull') : null,
|
|
28254
|
+
mergedName: spread ? allocCompilerName(ctx, '__styleObject') : null,
|
|
28255
|
+
valueNames: spread
|
|
28256
|
+
? evaluations.map(() => allocCompilerName(ctx, '__styleValue'))
|
|
28257
|
+
: null,
|
|
26433
28258
|
properties: [...propertyBindings.values()],
|
|
26434
28259
|
evaluations,
|
|
26435
28260
|
path,
|
|
@@ -26559,7 +28384,7 @@ function emitElementHtml(
|
|
|
26559
28384
|
} else if (attrName === 'class') {
|
|
26560
28385
|
// (`className` was already normalized to `class` above.)
|
|
26561
28386
|
const scalar = firstSpreadIdx === -1 ? scalarStateClass(inner, ctx) : null;
|
|
26562
|
-
|
|
28387
|
+
const binding = {
|
|
26563
28388
|
id: bindings.length,
|
|
26564
28389
|
kind: 'class',
|
|
26565
28390
|
expr: scalar === null ? expr : tsrxExprNode(scalar, ctx, componentName, inlinedSubs),
|
|
@@ -26567,7 +28392,9 @@ function emitElementHtml(
|
|
|
26567
28392
|
ns: hostNs,
|
|
26568
28393
|
fresh: scalar === null && isFreshBindingExpr(inner),
|
|
26569
28394
|
nameOrigin: attr.name,
|
|
26570
|
-
|
|
28395
|
+
name: 'class',
|
|
28396
|
+
};
|
|
28397
|
+
bindings.push(markDirectSignalBinding(binding, ctx, inner, 'binding'));
|
|
26571
28398
|
} else if (
|
|
26572
28399
|
(tag === 'form' && attrName === 'action') ||
|
|
26573
28400
|
((tag === 'button' || tag === 'input') &&
|
|
@@ -26592,15 +28419,22 @@ function emitElementHtml(
|
|
|
26592
28419
|
// namespace, URL, controlled-form, and validity routing. Their value
|
|
26593
28420
|
// contract is uniform across native/custom HTML, SVG, and MathML:
|
|
26594
28421
|
// nullish removes and everything else stringifies.
|
|
26595
|
-
bindings.push(
|
|
26596
|
-
|
|
26597
|
-
|
|
26598
|
-
|
|
26599
|
-
|
|
26600
|
-
|
|
26601
|
-
|
|
26602
|
-
|
|
26603
|
-
|
|
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
|
+
);
|
|
26604
28438
|
} else if (
|
|
26605
28439
|
!((hostNs === 'html' || hostNs === 'opaque') && tag.includes('-')) &&
|
|
26606
28440
|
BOOLEAN_ATTR_PROPS.has(attrName.toLowerCase())
|
|
@@ -26608,15 +28442,22 @@ function emitElementHtml(
|
|
|
26608
28442
|
// Exact native boolean props all share the canonical presence contract.
|
|
26609
28443
|
// The lowercase name is the final DOM spelling (including camelCase JSX
|
|
26610
28444
|
// aliases such as allowFullScreen -> allowfullscreen).
|
|
26611
|
-
bindings.push(
|
|
26612
|
-
|
|
26613
|
-
|
|
26614
|
-
|
|
26615
|
-
|
|
26616
|
-
|
|
26617
|
-
|
|
26618
|
-
|
|
26619
|
-
|
|
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
|
+
);
|
|
26620
28461
|
} else if (
|
|
26621
28462
|
/^data-[a-z][a-z0-9_-]*$/.test(attrName) &&
|
|
26622
28463
|
isKnownStringExpression(inner, ctx.knownStringLocals)
|
|
@@ -26627,33 +28468,57 @@ function emitElementHtml(
|
|
|
26627
28468
|
// data attribute in HTML, SVG, and MathML, so destination-opaque component
|
|
26628
28469
|
// templates can retain this specialization. Production also admits unknown
|
|
26629
28470
|
// scalar data values through staticAttributeWriter below.
|
|
26630
|
-
bindings.push(
|
|
26631
|
-
|
|
26632
|
-
|
|
26633
|
-
|
|
26634
|
-
|
|
26635
|
-
|
|
26636
|
-
|
|
26637
|
-
|
|
26638
|
-
|
|
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
|
+
);
|
|
26639
28487
|
} else {
|
|
26640
28488
|
const writer = ctx.dev ? null : staticAttributeWriter(tag, attrName);
|
|
26641
|
-
bindings.push(
|
|
26642
|
-
|
|
26643
|
-
|
|
26644
|
-
|
|
26645
|
-
|
|
26646
|
-
|
|
26647
|
-
|
|
26648
|
-
|
|
26649
|
-
|
|
26650
|
-
|
|
26651
|
-
|
|
26652
|
-
|
|
26653
|
-
|
|
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
|
+
);
|
|
26654
28509
|
}
|
|
26655
28510
|
}
|
|
26656
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
|
+
}
|
|
26657
28522
|
hostCommitClientBinding = {
|
|
26658
28523
|
id: bindings.length,
|
|
26659
28524
|
kind: 'hostCommit',
|
|
@@ -26661,6 +28526,8 @@ function emitElementHtml(
|
|
|
26661
28526
|
origin: node,
|
|
26662
28527
|
sources: hostClientSources,
|
|
26663
28528
|
hasNestedChildren: hasNestedJsxChildren,
|
|
28529
|
+
signalHostSources,
|
|
28530
|
+
signalSite: signalHostSources ? hostSignalSite : null,
|
|
26664
28531
|
};
|
|
26665
28532
|
bindings.push(hostCommitClientBinding);
|
|
26666
28533
|
} else if (resolveDangerouslySetInnerHTMLAcrossSpreads) {
|
|
@@ -26736,13 +28603,16 @@ function emitElementHtml(
|
|
|
26736
28603
|
}
|
|
26737
28604
|
}
|
|
26738
28605
|
if (
|
|
28606
|
+
!VOID_ELEMENTS.has(tag) &&
|
|
26739
28607
|
!hasNestedJsxChildren &&
|
|
26740
28608
|
(hostCommitClientBinding !== null || directChildrenClientBinding !== null)
|
|
26741
28609
|
) {
|
|
26742
28610
|
compCalls.push({
|
|
26743
28611
|
id: ctx.nextHelperId++,
|
|
26744
28612
|
loc: devLoc(ctx, node),
|
|
28613
|
+
origin: node,
|
|
26745
28614
|
isChild: true,
|
|
28615
|
+
signalSite: directSignalSite(ctx, node, 'binding'),
|
|
26746
28616
|
hostPath: path,
|
|
26747
28617
|
onlyChildText: true,
|
|
26748
28618
|
potentialDangerouslySetInnerHTML,
|
|
@@ -26787,7 +28657,14 @@ function emitElementHtml(
|
|
|
26787
28657
|
if (hasNestedHoist) children = children.filter((n) => n.type !== 'HeadHoist');
|
|
26788
28658
|
}
|
|
26789
28659
|
// Special case: a single Text child (only-child text fast path).
|
|
26790
|
-
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
|
+
) {
|
|
26791
28668
|
const txtChild = children[0];
|
|
26792
28669
|
const staticLit = staticTextLiteral(txtChild.expression);
|
|
26793
28670
|
if (staticLit !== null) {
|
|
@@ -26811,15 +28688,23 @@ function emitElementHtml(
|
|
|
26811
28688
|
]
|
|
26812
28689
|
: null;
|
|
26813
28690
|
appendTemplatePart(html, escaped, 'text', origins);
|
|
26814
|
-
} 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
|
+
);
|
|
26815
28706
|
const seededText = tag !== 'template' && !tag.includes('-') && !directPropNames.has('is');
|
|
26816
|
-
bindings.push({
|
|
26817
|
-
id: bindings.length,
|
|
26818
|
-
kind: 'textOnlyChild',
|
|
26819
|
-
expr: resolveStyleExpr(txtChild.expression, cssHash),
|
|
26820
|
-
path,
|
|
26821
|
-
seededText,
|
|
26822
|
-
});
|
|
28707
|
+
bindings.push({ ...binding, seededText });
|
|
26823
28708
|
// A nonempty placeholder survives the HTML parser as one Text node. The
|
|
26824
28709
|
// mount writes its actual value before the cloned host is inserted, so
|
|
26825
28710
|
// even an empty value retains one stable text binding node. <template>
|
|
@@ -26961,8 +28846,23 @@ function emitElementHtml(
|
|
|
26961
28846
|
childIdx++;
|
|
26962
28847
|
continue;
|
|
26963
28848
|
}
|
|
26964
|
-
if (child.
|
|
26965
|
-
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);
|
|
26966
28866
|
if (staticLit === '') {
|
|
26967
28867
|
// Renders nothing: bake nothing and consume no childIndex (an empty
|
|
26968
28868
|
// text produces NO node when the template HTML is parsed, so counting
|
|
@@ -26994,17 +28894,30 @@ function emitElementHtml(
|
|
|
26994
28894
|
appendTemplatePart(html, escaped, 'text', origins);
|
|
26995
28895
|
prevBakedText = true;
|
|
26996
28896
|
if (!prevBaked) childIdx++;
|
|
26997
|
-
} else if (
|
|
26998
|
-
|
|
26999
|
-
|
|
27000
|
-
|
|
27001
|
-
|
|
27002
|
-
|
|
27003
|
-
|
|
27004
|
-
|
|
27005
|
-
|
|
27006
|
-
|
|
27007
|
-
|
|
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
|
+
);
|
|
27008
28921
|
appendTemplatePart(html, '<!>', 'anchor'); // placeholder we'll replace at mount
|
|
27009
28922
|
childIdx++;
|
|
27010
28923
|
} else {
|
|
@@ -27183,24 +29096,36 @@ function emitElementHtml(
|
|
|
27183
29096
|
} else if (isConditionalJsx(expr)) {
|
|
27184
29097
|
// Lower `{cond ? A : B}` (where A or B is JSX) to an IfStatement so
|
|
27185
29098
|
// each branch renders real DOM via the existing ifBlock machinery.
|
|
27186
|
-
const asIf =
|
|
27187
|
-
...b.if(expr.test, wrapAsBlockStmt(expr.consequent), wrapAsBlockStmt(expr.alternate)),
|
|
27188
|
-
loc: expr.loc, // carry source position for dev hydration-mismatch LOC
|
|
27189
|
-
};
|
|
29099
|
+
const asIf = conditionalJsxStatement(expr);
|
|
27190
29100
|
const ic = makeIfCall(asIf, ctx, inlinedSubs, childNs, cssHash);
|
|
27191
29101
|
ic.hostPath = path;
|
|
27192
29102
|
ic.anchorPath = [...path, childIdx];
|
|
27193
29103
|
ifCalls.push(ic);
|
|
27194
29104
|
appendTemplatePart(html, '<!>', 'anchor');
|
|
27195
29105
|
childIdx++;
|
|
27196
|
-
} else if (
|
|
27197
|
-
|
|
27198
|
-
|
|
27199
|
-
|
|
27200
|
-
|
|
27201
|
-
|
|
27202
|
-
|
|
27203
|
-
|
|
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
|
+
);
|
|
27204
29129
|
appendTemplatePart(html, '<!>', 'anchor');
|
|
27205
29130
|
childIdx++;
|
|
27206
29131
|
} else {
|
|
@@ -27409,6 +29334,17 @@ function hoistBodyHelper(
|
|
|
27409
29334
|
id: b.id(helperName, idOrigin ?? fakeOrigin ?? undefined),
|
|
27410
29335
|
params: params || [],
|
|
27411
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
|
+
: {}),
|
|
27412
29348
|
},
|
|
27413
29349
|
fakeOrigin,
|
|
27414
29350
|
);
|
|
@@ -27553,6 +29489,7 @@ function makeIfCall(node, ctx, inlinedSubs, parentNs = 'html', cssHash = null) {
|
|
|
27553
29489
|
origin: node,
|
|
27554
29490
|
condExpr: node.test, // the fold replaces this with a `props.hN` member read
|
|
27555
29491
|
condTest: node.test, // raw test AST — the fold threads it as a `props.hN` hole
|
|
29492
|
+
bindingSite: node._octaneBindingSite,
|
|
27556
29493
|
envNames,
|
|
27557
29494
|
thenHelper: thenHelperName,
|
|
27558
29495
|
elseHelper: elseHelperName,
|
|
@@ -27801,6 +29738,7 @@ function makeChildCall(expr, ctx, componentName, inlinedSubs, cssHash, parentNs
|
|
|
27801
29738
|
loc: devLoc(ctx, expr),
|
|
27802
29739
|
origin: expr,
|
|
27803
29740
|
isChild: true,
|
|
29741
|
+
signalSite: directSignalSite(ctx, expr, 'binding'),
|
|
27804
29742
|
valueExpr: tsrxExprNode(
|
|
27805
29743
|
resolveStyleExpr(rewriteChildHoleValue(expr, ctx), cssHash),
|
|
27806
29744
|
ctx,
|
|
@@ -28010,6 +29948,7 @@ function makeCompCall(
|
|
|
28010
29948
|
cssHash = null,
|
|
28011
29949
|
) {
|
|
28012
29950
|
const id = ctx.nextHelperId++;
|
|
29951
|
+
const invocationSite = componentInvocationSite(ctx, node);
|
|
28013
29952
|
const compName = tagBindingName(node);
|
|
28014
29953
|
const staticFragmentRenderer = node.openingElement?.metadata?.staticFragmentRenderer;
|
|
28015
29954
|
const activityDescriptor = isActivityLongForm(node, ctx);
|
|
@@ -28149,23 +30088,31 @@ function makeCompCall(
|
|
|
28149
30088
|
ctx.hoistedHelpers.push(inheritOriginLoc(b.const(bodyIdentity, b.object([])), node));
|
|
28150
30089
|
childrenArgs.push(b.id(bodyIdentity));
|
|
28151
30090
|
}
|
|
28152
|
-
|
|
28153
|
-
|
|
28154
|
-
|
|
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));
|
|
28155
30099
|
}
|
|
28156
30100
|
}
|
|
28157
30101
|
|
|
28158
30102
|
// The props object as a node; the call-site emit embeds it directly.
|
|
28159
30103
|
const propsExpr = staticFragmentRenderer?.props ?? inheritOriginLoc(b.object(propNodes), node);
|
|
28160
30104
|
if (descriptorConfig) {
|
|
28161
|
-
ctx.runtimeNeeded.add('
|
|
30105
|
+
ctx.runtimeNeeded.add('createElementFromConfig');
|
|
28162
30106
|
return {
|
|
28163
30107
|
id,
|
|
28164
30108
|
loc: devLoc(ctx, node),
|
|
28165
30109
|
origin: node,
|
|
28166
30110
|
isChild: true,
|
|
28167
30111
|
anchorlessAppendSafe: true,
|
|
28168
|
-
valueExpr: inheritOriginLoc(
|
|
30112
|
+
valueExpr: inheritOriginLoc(
|
|
30113
|
+
b.call('_$createElementFromConfig', b.literal(invocationSite), compNode, propsExpr),
|
|
30114
|
+
node,
|
|
30115
|
+
),
|
|
28169
30116
|
};
|
|
28170
30117
|
}
|
|
28171
30118
|
|
|
@@ -28341,11 +30288,13 @@ function makeCompCall(
|
|
|
28341
30288
|
|
|
28342
30289
|
return {
|
|
28343
30290
|
id,
|
|
30291
|
+
invocationSite,
|
|
28344
30292
|
compNode,
|
|
28345
30293
|
propsExpr,
|
|
28346
30294
|
hostPath: null,
|
|
28347
30295
|
keyExpr,
|
|
28348
|
-
|
|
30296
|
+
bindingSite: node._octaneBindingSite,
|
|
30297
|
+
liteEligible: node._octaneBindingSite ? false : liteEligible,
|
|
28349
30298
|
anchorlessAppendSafe,
|
|
28350
30299
|
autoMemoDeps,
|
|
28351
30300
|
autoMemoDepNodes,
|
|
@@ -28354,8 +30303,8 @@ function makeCompCall(
|
|
|
28354
30303
|
autoMemoLite,
|
|
28355
30304
|
autoMemoPublicationWitnesses,
|
|
28356
30305
|
voidComponent,
|
|
28357
|
-
singleRoot,
|
|
28358
|
-
maybeSingleRoot,
|
|
30306
|
+
singleRoot: node._octaneBindingSite ? false : singleRoot,
|
|
30307
|
+
maybeSingleRoot: node._octaneBindingSite ? false : maybeSingleRoot,
|
|
28359
30308
|
loc: devLoc(ctx, node),
|
|
28360
30309
|
origin: node,
|
|
28361
30310
|
};
|
|
@@ -28662,7 +30611,7 @@ function makeForCall(node, ctx, inlinedSubs, parentNs = 'html', cssHash = null)
|
|
|
28662
30611
|
'`use(promise)` first.',
|
|
28663
30612
|
);
|
|
28664
30613
|
}
|
|
28665
|
-
// node.left = const x OR const
|
|
30614
|
+
// node.left = const x OR const {x,y} / const [a,b] (destructured)
|
|
28666
30615
|
// node.right = expr, node.body = BlockStatement,
|
|
28667
30616
|
// node.key = optional `key …` expression, node.index = optional `index <id>`.
|
|
28668
30617
|
// `@for (...) { ... } @empty { ... }` — hoist the empty branch as its own
|
|
@@ -28678,7 +30627,7 @@ function makeForCall(node, ctx, inlinedSubs, parentNs = 'html', cssHash = null)
|
|
|
28678
30627
|
const leftDeclId = node.left.declarations[0].id;
|
|
28679
30628
|
const isDestructured = leftDeclId.type !== 'Identifier';
|
|
28680
30629
|
// `itemName` is the identifier used in the body signature + keyFn. For a
|
|
28681
|
-
// plain `const x of …`, that's `x`. For a destructured `const
|
|
30630
|
+
// plain `const x of …`, that's `x`. For a destructured `const {id} of …`,
|
|
28682
30631
|
// we synthesize a fresh name and emit the destructuring inside the body so
|
|
28683
30632
|
// the keyFn still gets the whole item and the body still sees the fields.
|
|
28684
30633
|
const itemName = isDestructured ? '_item' : leftDeclId.name;
|
|
@@ -28785,7 +30734,7 @@ function makeForCall(node, ctx, inlinedSubs, parentNs = 'html', cssHash = null)
|
|
|
28785
30734
|
]
|
|
28786
30735
|
: [];
|
|
28787
30736
|
|
|
28788
|
-
// Destructured header `const
|
|
30737
|
+
// Destructured header `const {x,y} of …` — synthesize a destructure stmt
|
|
28789
30738
|
// at the top of the body so the user fields bind from the synthetic item.
|
|
28790
30739
|
const destructureInjection = isDestructured
|
|
28791
30740
|
? [
|
|
@@ -29133,6 +31082,10 @@ function makeForCall(node, ctx, inlinedSubs, parentNs = 'html', cssHash = null)
|
|
|
29133
31082
|
}
|
|
29134
31083
|
}
|
|
29135
31084
|
|
|
31085
|
+
if (node._octaneBindingSite) {
|
|
31086
|
+
singleRoot = false;
|
|
31087
|
+
singleRootExpr = null;
|
|
31088
|
+
}
|
|
29136
31089
|
const ssrMarkerless = isSsrMarkerlessForItem(node);
|
|
29137
31090
|
const hostRootTag = subStmts[0]?.id?.name ?? subStmts[0]?.openingElement?.name?.name;
|
|
29138
31091
|
const hostMountSafe =
|