octane 0.3.0 → 0.3.2
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/dist/cjs/constants.cjs +18 -25
- package/dist/cjs/css.cjs +6 -0
- package/dist/cjs/dom-binding-protocol.cjs +37 -14
- package/dist/cjs/hydration/control-capture.cjs +12 -4
- package/dist/cjs/hydration/event-capture.cjs +6 -7
- package/dist/cjs/hydration-markers.cjs +40 -2
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/runtime-tags.cjs +55 -0
- package/dist/cjs/runtime.cjs +235 -148
- package/dist/cjs/runtime.server.cjs +110 -80
- package/dist/cjs/server/streamed-signals.cjs +102 -64
- package/dist/cjs/signals/control-binding.cjs +2 -1
- package/dist/cjs/stream-protocol.cjs +9 -8
- package/dist/cjs/version.cjs +1 -1
- package/dist/compiler/compile.js +550 -35
- package/dist/compiler/dom-bindings.js +5 -0
- package/dist/compiler/hydrate-boundaries.js +118 -0
- package/dist/compiler/private-context.js +160 -0
- package/dist/constants.d.ts +7 -18
- package/dist/constants.js +11 -9
- package/dist/css.d.ts +7 -0
- package/dist/css.js +4 -0
- package/dist/dom-binding-program.js +12 -5
- package/dist/dom-binding-protocol.d.ts +14 -1
- package/dist/dom-binding-protocol.js +40 -14
- package/dist/hydration/control-capture.d.ts +2 -0
- package/dist/hydration/control-capture.js +18 -5
- package/dist/hydration/event-capture.js +1 -2
- package/dist/hydration/independent-island.js +5 -5
- package/dist/hydration/stream-delivery.d.ts +1 -1
- package/dist/hydration/stream-delivery.js +16 -9
- package/dist/hydration/stream-result-receiver.d.ts +1 -0
- package/dist/hydration/stream-result-receiver.js +8 -4
- package/dist/hydration-markers.d.ts +30 -0
- package/dist/hydration-markers.js +27 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -0
- package/dist/react/index.js +1 -1
- package/dist/react/react-compat-shared.js +5 -3
- package/dist/react/shared.d.ts +1 -3
- package/dist/react/shared.js +1 -1
- package/dist/runtime-tags.d.ts +34 -0
- package/dist/runtime-tags.js +22 -0
- package/dist/runtime.d.ts +9 -14
- package/dist/runtime.js +213 -106
- package/dist/runtime.server.d.ts +10 -5
- package/dist/runtime.server.js +101 -56
- package/dist/server/streamed-signals.d.ts +1 -0
- package/dist/server/streamed-signals.js +102 -64
- package/dist/signals/control-binding.js +4 -2
- package/dist/stream-protocol.js +20 -9
- package/dist/universal-core.js +8 -4
- package/dist/universal-native.js +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
package/dist/compiler/compile.js
CHANGED
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
analyzeCallbackDependencies,
|
|
57
57
|
analyzeHookDependencies,
|
|
58
58
|
applyHookDependencies,
|
|
59
|
+
collectReassignedBindings,
|
|
59
60
|
isInvariantLiteral,
|
|
60
61
|
} from './hook-deps.js';
|
|
61
62
|
import {
|
|
@@ -83,6 +84,8 @@ import {
|
|
|
83
84
|
prepareServerRendererBoundaryRegions,
|
|
84
85
|
} from './compile-renderer-boundaries.js';
|
|
85
86
|
import {
|
|
87
|
+
compiledSplitHydrateTagsForAst,
|
|
88
|
+
privateCompiledContextsForHydrateAst,
|
|
86
89
|
hydrateBoundaryPathFromId,
|
|
87
90
|
prepareHydrateBoundaries,
|
|
88
91
|
prepareServerHydrateBoundaries,
|
|
@@ -125,6 +128,7 @@ import { assertNoLegacyContextProviders } from './context-provider.js';
|
|
|
125
128
|
import { applyCssModuleConstants } from './css-module-constants.js';
|
|
126
129
|
import { assertUniversalRuntimeTarget, normalizeUniversalRuntime } from './universal-runtime.js';
|
|
127
130
|
import { findLocalVoidRootCallees } from './local-void-roots.js';
|
|
131
|
+
import { findPrivateCompiledContexts } from './private-context.js';
|
|
128
132
|
|
|
129
133
|
// DOM truth tables shared with the client/server runtimes (via constants.ts) —
|
|
130
134
|
// static bakes and dynamic writes MUST agree on which attributes render, under
|
|
@@ -535,12 +539,17 @@ function attrBindingUpdateHelper(bind, inlineBindingGuards = false) {
|
|
|
535
539
|
}
|
|
536
540
|
}
|
|
537
541
|
|
|
538
|
-
function canCarryDirectSignalHandle(
|
|
542
|
+
function canCarryDirectSignalHandle(
|
|
543
|
+
node,
|
|
544
|
+
conservativeResult = false,
|
|
545
|
+
retainLocalCapability = false,
|
|
546
|
+
) {
|
|
539
547
|
if (
|
|
540
548
|
!node ||
|
|
541
549
|
node.metadata?.octane_string_child ||
|
|
542
550
|
node.metadata?.octane_primitive_text_child ||
|
|
543
|
-
node.metadata?.octane_primitive_value
|
|
551
|
+
(node.metadata?.octane_primitive_value &&
|
|
552
|
+
!(retainLocalCapability && node.metadata?.octane_primitive_local_value))
|
|
544
553
|
) {
|
|
545
554
|
return false;
|
|
546
555
|
}
|
|
@@ -551,22 +560,26 @@ function canCarryDirectSignalHandle(node, conservativeResult = false) {
|
|
|
551
560
|
node.type === 'ParenthesizedExpression' ||
|
|
552
561
|
node.type === 'ChainExpression'
|
|
553
562
|
) {
|
|
554
|
-
return canCarryDirectSignalHandle(node.expression, conservativeResult);
|
|
563
|
+
return canCarryDirectSignalHandle(node.expression, conservativeResult, retainLocalCapability);
|
|
555
564
|
}
|
|
556
565
|
if (node.type === 'ConditionalExpression') {
|
|
557
566
|
return (
|
|
558
|
-
canCarryDirectSignalHandle(node.consequent, conservativeResult) ||
|
|
559
|
-
canCarryDirectSignalHandle(node.alternate, conservativeResult)
|
|
567
|
+
canCarryDirectSignalHandle(node.consequent, conservativeResult, retainLocalCapability) ||
|
|
568
|
+
canCarryDirectSignalHandle(node.alternate, conservativeResult, retainLocalCapability)
|
|
560
569
|
);
|
|
561
570
|
}
|
|
562
571
|
if (node.type === 'LogicalExpression') {
|
|
563
572
|
return (
|
|
564
|
-
canCarryDirectSignalHandle(node.left, conservativeResult) ||
|
|
565
|
-
canCarryDirectSignalHandle(node.right, conservativeResult)
|
|
573
|
+
canCarryDirectSignalHandle(node.left, conservativeResult, retainLocalCapability) ||
|
|
574
|
+
canCarryDirectSignalHandle(node.right, conservativeResult, retainLocalCapability)
|
|
566
575
|
);
|
|
567
576
|
}
|
|
568
577
|
if (node.type === 'SequenceExpression') {
|
|
569
|
-
return canCarryDirectSignalHandle(
|
|
578
|
+
return canCarryDirectSignalHandle(
|
|
579
|
+
node.expressions.at(-1),
|
|
580
|
+
conservativeResult,
|
|
581
|
+
retainLocalCapability,
|
|
582
|
+
);
|
|
570
583
|
}
|
|
571
584
|
if (
|
|
572
585
|
!conservativeResult &&
|
|
@@ -652,7 +665,16 @@ function componentInvocationSite(ctx, node) {
|
|
|
652
665
|
}
|
|
653
666
|
|
|
654
667
|
function markDirectSignalBinding(binding, ctx, origin, kind) {
|
|
655
|
-
if (!canCarryDirectSignalHandle(binding.expr))
|
|
668
|
+
if (!canCarryDirectSignalHandle(binding.expr)) {
|
|
669
|
+
// Newly certified locals still retain the prior potential capability:
|
|
670
|
+
// opaque events can import instance models after their first mount. A
|
|
671
|
+
// primitive result removes this adapter, not structural owner identity.
|
|
672
|
+
if (canCarryDirectSignalHandle(binding.expr, false, true)) {
|
|
673
|
+
ctx.signalBindingsUsed = true;
|
|
674
|
+
if (isDirectSignalHandleExpression(binding.expr)) ctx.signalBindingsEager = true;
|
|
675
|
+
}
|
|
676
|
+
return binding;
|
|
677
|
+
}
|
|
656
678
|
ctx.signalBindingsUsed = true;
|
|
657
679
|
if (isDirectSignalHandleExpression(binding.expr)) ctx.signalBindingsEager = true;
|
|
658
680
|
return {
|
|
@@ -664,6 +686,7 @@ function markDirectSignalBinding(binding, ctx, origin, kind) {
|
|
|
664
686
|
|
|
665
687
|
function ssrSignalValue(node, ctx, origin, capability = false) {
|
|
666
688
|
if (!(capability ? canCarryDirectSignalHandle(node) : isDirectSignalHandleExpression(node))) {
|
|
689
|
+
if (capability && canCarryDirectSignalHandle(node, false, true)) ctx.signalBindingsUsed = true;
|
|
667
690
|
return node;
|
|
668
691
|
}
|
|
669
692
|
ctx.signalBindingsUsed = true;
|
|
@@ -1401,6 +1424,7 @@ const NATIVE_READ_RUNTIME_HELPERS = new Set([
|
|
|
1401
1424
|
'nativeCreateScopedElement',
|
|
1402
1425
|
]);
|
|
1403
1426
|
const INTERNAL_CLIENT_RUNTIME_HELPERS = new Set([
|
|
1427
|
+
'__createCompiledContext',
|
|
1404
1428
|
'isContext',
|
|
1405
1429
|
'bindPresentationView',
|
|
1406
1430
|
'beginPresentationHydration',
|
|
@@ -1420,6 +1444,7 @@ const INTERNAL_CLIENT_RUNTIME_HELPERS = new Set([
|
|
|
1420
1444
|
'bindSignalText',
|
|
1421
1445
|
'bindSignalChild',
|
|
1422
1446
|
'bindSignalAttribute',
|
|
1447
|
+
'hydrateClaimedBindingCaches',
|
|
1423
1448
|
'bindSignalValue',
|
|
1424
1449
|
'bindSignalChecked',
|
|
1425
1450
|
'bindSignalHostPropSources',
|
|
@@ -9921,6 +9946,12 @@ function compileInternal(
|
|
|
9921
9946
|
options?.rendererBoundaries == null &&
|
|
9922
9947
|
options?.rendererRegistry == null;
|
|
9923
9948
|
const authoredVoidRootIds = new Set();
|
|
9949
|
+
const privateCompiledContexts = localVoidRootsEnabled
|
|
9950
|
+
? findPrivateCompiledContexts(ast)
|
|
9951
|
+
: new Map();
|
|
9952
|
+
const splitPrivateContexts = localVoidRootsEnabled
|
|
9953
|
+
? privateCompiledContextsForHydrateAst(parsedAst)
|
|
9954
|
+
: null;
|
|
9924
9955
|
if (
|
|
9925
9956
|
localVoidRootsEnabled &&
|
|
9926
9957
|
ast.body.some(
|
|
@@ -10066,6 +10097,9 @@ function compileInternal(
|
|
|
10066
10097
|
nativeChangeClassifications: nativeChangeAnalysis.classifications,
|
|
10067
10098
|
dev: devEnabled,
|
|
10068
10099
|
profile: profileEnabled,
|
|
10100
|
+
compiledHydrateTemplates: localVoidRootsEnabled,
|
|
10101
|
+
compiledSplitHydrateTags: compiledSplitHydrateTagsForAst(parsedAst),
|
|
10102
|
+
privateSplitContextProviders: splitPrivateContexts?.providers,
|
|
10069
10103
|
autoMemo: autoMemoEnabled,
|
|
10070
10104
|
strongMemo: strongMemoEnabled,
|
|
10071
10105
|
nativeReads: options?.nativeReads === true,
|
|
@@ -10450,6 +10484,18 @@ function compileInternal(
|
|
|
10450
10484
|
);
|
|
10451
10485
|
}
|
|
10452
10486
|
}
|
|
10487
|
+
if (privateCompiledContexts.size > 0 || splitPrivateContexts?.callees.size > 0) {
|
|
10488
|
+
const helper = '__createCompiledContext';
|
|
10489
|
+
const alias = allocCompilerName(ctx, rtAlias(helper));
|
|
10490
|
+
(ctx.privateRuntimeAliases ??= new Map()).set(helper, alias);
|
|
10491
|
+
ctx.runtimeNeeded.add(helper);
|
|
10492
|
+
const callees = new Set([
|
|
10493
|
+
...privateCompiledContexts.values(),
|
|
10494
|
+
...(splitPrivateContexts?.callees ?? []),
|
|
10495
|
+
]);
|
|
10496
|
+
ast = mapAst(ast, (node) => (callees.has(node) ? { ...node, name: alias } : null));
|
|
10497
|
+
ctx.privateCompiledContexts = new Set(privateCompiledContexts.keys());
|
|
10498
|
+
}
|
|
10453
10499
|
// Return-based JSX functions never attach fetch-tree warm plans, and a sole
|
|
10454
10500
|
// same-module component already rejects a self-only warm edge below. Avoid a
|
|
10455
10501
|
// whole-module graph walk unless a compiled-void body can actually reach a
|
|
@@ -12719,8 +12765,10 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12719
12765
|
);
|
|
12720
12766
|
const hostSignalSite = directSignalSite(ctx, node, 'binding');
|
|
12721
12767
|
let directSignalControlSite = null;
|
|
12768
|
+
let directSignalControlValues = false;
|
|
12722
12769
|
const markDirectControl = (expression, origin) => {
|
|
12723
|
-
|
|
12770
|
+
const carriesHandle = canCarryDirectSignalHandle(expression);
|
|
12771
|
+
if (!carriesHandle && !canCarryDirectSignalHandle(expression, false, true)) return expression;
|
|
12724
12772
|
const site = firstSpreadIdx === -1 ? directSignalSite(ctx, node, 'input') : hostSignalSite;
|
|
12725
12773
|
if (directSignalControlSite === null) {
|
|
12726
12774
|
if (directAttributeIdentities.has('data-octane-input')) {
|
|
@@ -12732,10 +12780,13 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
12732
12780
|
bakeLit(` data-octane-input="${escapeAttr(site)}"`, 'data-octane-input', null, null);
|
|
12733
12781
|
}
|
|
12734
12782
|
ctx.signalBindingsUsed = true;
|
|
12783
|
+
if (!carriesHandle) return expression;
|
|
12784
|
+
directSignalControlValues = true;
|
|
12735
12785
|
ctx.runtimeNeeded.add('ssrSignalControlValue');
|
|
12736
12786
|
return ssrCall('ssrSignalControlValue', [expression, b.literal(site)], origin);
|
|
12737
12787
|
};
|
|
12738
12788
|
if (firstSpreadIdx !== -1 && (tag === 'input' || tag === 'textarea' || tag === 'select')) {
|
|
12789
|
+
directSignalControlValues = true;
|
|
12739
12790
|
directSignalControlSite = hostSignalSite;
|
|
12740
12791
|
if (directAttributeIdentities.has('data-octane-input')) {
|
|
12741
12792
|
throw new Error(
|
|
@@ -13414,7 +13465,7 @@ function ssrEmitElement(node, ctx, name, inlinedSubs, parentNs, cssHash, compone
|
|
|
13414
13465
|
parts.push(singleDirectAttribute);
|
|
13415
13466
|
}
|
|
13416
13467
|
let signalControlAttrs = null;
|
|
13417
|
-
if (
|
|
13468
|
+
if (directSignalControlValues) {
|
|
13418
13469
|
ctx.runtimeNeeded.add('ssrSignalControlAttrs');
|
|
13419
13470
|
signalControlAttrs = ssrCall('ssrSignalControlAttrs', [b.array(formControlSources)], node);
|
|
13420
13471
|
}
|
|
@@ -15540,6 +15591,8 @@ function preparePresentationHydration(body, node, ctx, hostEnd) {
|
|
|
15540
15591
|
* bodies. `cssHash` selects the enclosing scoped-style expression fallback.
|
|
15541
15592
|
*/
|
|
15542
15593
|
function compileFunctionBody(node, ctx, name, parentNs = 'html', cssHash = null, options = null) {
|
|
15594
|
+
const previousScalarBindingClaims = ctx.scalarBindingClaims;
|
|
15595
|
+
ctx.scalarBindingClaims = node._octaneScalarBindingClaims;
|
|
15543
15596
|
const previousPresentationHydration = ctx.presentationHydration;
|
|
15544
15597
|
ctx.presentationHydration = node._octanePresentationHydration;
|
|
15545
15598
|
const returnedOutput = options?.returnedOutput === true;
|
|
@@ -16153,6 +16206,7 @@ function compileFunctionBody(node, ctx, name, parentNs = 'html', cssHash = null,
|
|
|
16153
16206
|
ctx,
|
|
16154
16207
|
presentationHostEnd,
|
|
16155
16208
|
);
|
|
16209
|
+
ctx.scalarBindingClaims = previousScalarBindingClaims;
|
|
16156
16210
|
ctx.presentationHydration = previousPresentationHydration;
|
|
16157
16211
|
const emittedFunction = b.function_declaration(
|
|
16158
16212
|
b.id(name, node.id ?? node),
|
|
@@ -18299,7 +18353,12 @@ function buildWarmArtifacts(node, ctx, componentName, creations, warmChildren) {
|
|
|
18299
18353
|
inheritOriginLoc(
|
|
18300
18354
|
b.call(
|
|
18301
18355
|
warmChildAlias,
|
|
18302
|
-
b.id(
|
|
18356
|
+
b.id(
|
|
18357
|
+
ctx.compiledSplitHydrateTags?.has(w.origin.openingElement?.name) &&
|
|
18358
|
+
isCompiledHydrateTemplate(w.origin, ctx, w.origin.openingElement.attributes)
|
|
18359
|
+
? requireCompiledHydrateAlias(ctx)
|
|
18360
|
+
: w.compName,
|
|
18361
|
+
),
|
|
18303
18362
|
b.object(
|
|
18304
18363
|
w.props.map((p) =>
|
|
18305
18364
|
b.prop(
|
|
@@ -20107,13 +20166,22 @@ function memberProps(hn, src) {
|
|
|
20107
20166
|
// primitive value proofs separately from the renderer's text assertions:
|
|
20108
20167
|
// an authored `as string` still permits a signal handle at runtime.
|
|
20109
20168
|
...(isPrimitiveValueExpression(src)
|
|
20110
|
-
? {
|
|
20169
|
+
? {
|
|
20170
|
+
metadata: {
|
|
20171
|
+
...src?.metadata,
|
|
20172
|
+
octane_primitive_value: true,
|
|
20173
|
+
...(canCarryDirectSignalHandle(src, false, true) && !canCarryDirectSignalHandle(src)
|
|
20174
|
+
? { octane_primitive_local_value: true }
|
|
20175
|
+
: null),
|
|
20176
|
+
},
|
|
20177
|
+
}
|
|
20111
20178
|
: null),
|
|
20112
20179
|
};
|
|
20113
20180
|
}
|
|
20114
20181
|
|
|
20115
|
-
function isPrimitiveValueExpression(node) {
|
|
20182
|
+
function isPrimitiveValueExpression(node, proveExpression = null) {
|
|
20116
20183
|
if (!node || typeof node !== 'object') return false;
|
|
20184
|
+
if (proveExpression?.(node) === true) return true;
|
|
20117
20185
|
if (
|
|
20118
20186
|
node.type === 'TSAsExpression' ||
|
|
20119
20187
|
node.type === 'TSTypeAssertion' ||
|
|
@@ -20123,7 +20191,7 @@ function isPrimitiveValueExpression(node) {
|
|
|
20123
20191
|
node.type === 'ParenthesizedExpression' ||
|
|
20124
20192
|
node.type === 'ChainExpression'
|
|
20125
20193
|
) {
|
|
20126
|
-
return isPrimitiveValueExpression(node.expression);
|
|
20194
|
+
return isPrimitiveValueExpression(node.expression, proveExpression);
|
|
20127
20195
|
}
|
|
20128
20196
|
if (
|
|
20129
20197
|
node.metadata?.octane_string_child ||
|
|
@@ -20153,17 +20221,21 @@ function isPrimitiveValueExpression(node) {
|
|
|
20153
20221
|
}
|
|
20154
20222
|
if (node.type === 'ConditionalExpression') {
|
|
20155
20223
|
return (
|
|
20156
|
-
isPrimitiveValueExpression(node.consequent) &&
|
|
20224
|
+
isPrimitiveValueExpression(node.consequent, proveExpression) &&
|
|
20225
|
+
isPrimitiveValueExpression(node.alternate, proveExpression)
|
|
20157
20226
|
);
|
|
20158
20227
|
}
|
|
20159
20228
|
if (node.type === 'LogicalExpression') {
|
|
20160
|
-
return
|
|
20229
|
+
return (
|
|
20230
|
+
isPrimitiveValueExpression(node.left, proveExpression) &&
|
|
20231
|
+
isPrimitiveValueExpression(node.right, proveExpression)
|
|
20232
|
+
);
|
|
20161
20233
|
}
|
|
20162
20234
|
if (node.type === 'SequenceExpression') {
|
|
20163
|
-
return isPrimitiveValueExpression(node.expressions.at(-1));
|
|
20235
|
+
return isPrimitiveValueExpression(node.expressions.at(-1), proveExpression);
|
|
20164
20236
|
}
|
|
20165
20237
|
if (node.type === 'AssignmentExpression') {
|
|
20166
|
-
if (node.operator === '=') return isPrimitiveValueExpression(node.right);
|
|
20238
|
+
if (node.operator === '=') return isPrimitiveValueExpression(node.right, proveExpression);
|
|
20167
20239
|
return node.operator === '+=' || NUMERIC_TEXT_OPERATORS.has(node.operator.slice(0, -1));
|
|
20168
20240
|
}
|
|
20169
20241
|
return false;
|
|
@@ -23384,10 +23456,12 @@ function applyStringChildProofs(ast, source, filename, facts) {
|
|
|
23384
23456
|
source.includes('BigInt') ||
|
|
23385
23457
|
source.includes('Date') ||
|
|
23386
23458
|
source.includes('\\u');
|
|
23459
|
+
const inspectPrimitiveLocals = source.includes('const');
|
|
23387
23460
|
if (
|
|
23388
23461
|
(typedRanges === null || typedRanges.size === 0) &&
|
|
23389
23462
|
(primitiveRanges === null || primitiveRanges.size === 0) &&
|
|
23390
|
-
!inspectIntrinsicCalls
|
|
23463
|
+
!inspectIntrinsicCalls &&
|
|
23464
|
+
!inspectPrimitiveLocals
|
|
23391
23465
|
) {
|
|
23392
23466
|
return ast;
|
|
23393
23467
|
}
|
|
@@ -23395,7 +23469,12 @@ function applyStringChildProofs(ast, source, filename, facts) {
|
|
|
23395
23469
|
const primitiveProofs = new Set();
|
|
23396
23470
|
const intrinsicCalls = [];
|
|
23397
23471
|
const writes = [];
|
|
23472
|
+
let intrinsicMutationReference = false;
|
|
23473
|
+
const intrinsicMutationReferences = [];
|
|
23474
|
+
const intrinsicAliases = [];
|
|
23398
23475
|
const ambientIntrinsicValues = new Set();
|
|
23476
|
+
const constantDeclarations = [];
|
|
23477
|
+
const references = [];
|
|
23399
23478
|
const seen = new WeakSet();
|
|
23400
23479
|
const collect = (node, parent = null, key = null) => {
|
|
23401
23480
|
if (node === null || typeof node !== 'object') return;
|
|
@@ -23405,6 +23484,20 @@ function applyStringChildProofs(ast, source, filename, facts) {
|
|
|
23405
23484
|
}
|
|
23406
23485
|
if (seen.has(node)) return;
|
|
23407
23486
|
seen.add(node);
|
|
23487
|
+
if (inspectPrimitiveLocals) {
|
|
23488
|
+
if (
|
|
23489
|
+
node.type === 'VariableDeclarator' &&
|
|
23490
|
+
parent?.type === 'VariableDeclaration' &&
|
|
23491
|
+
parent.kind === 'const' &&
|
|
23492
|
+
parent.declare !== true &&
|
|
23493
|
+
node.id?.type === 'Identifier' &&
|
|
23494
|
+
node.init
|
|
23495
|
+
) {
|
|
23496
|
+
constantDeclarations.push(node);
|
|
23497
|
+
} else if (node.type === 'Identifier') {
|
|
23498
|
+
references.push({ node, parent, key });
|
|
23499
|
+
}
|
|
23500
|
+
}
|
|
23408
23501
|
if (
|
|
23409
23502
|
(typedRanges !== null || primitiveRanges !== null) &&
|
|
23410
23503
|
node.type === 'JSXExpressionContainer' &&
|
|
@@ -23421,9 +23514,43 @@ function applyStringChildProofs(ast, source, filename, facts) {
|
|
|
23421
23514
|
if (primitiveRanges?.delete(range)) primitiveProofs.add(expr);
|
|
23422
23515
|
}
|
|
23423
23516
|
if (inspectIntrinsicCalls) {
|
|
23517
|
+
if (
|
|
23518
|
+
node.type === 'MemberExpression' ||
|
|
23519
|
+
(node.type === 'Property' && parent?.type === 'ObjectPattern')
|
|
23520
|
+
) {
|
|
23521
|
+
const property = node.type === 'MemberExpression' ? node.property : node.key;
|
|
23522
|
+
const method = node.computed ? property?.value : (property?.name ?? property?.value);
|
|
23523
|
+
if (
|
|
23524
|
+
INTRINSIC_MUTATION_METHODS.has(method) ||
|
|
23525
|
+
(node.computed && property?.type !== 'Literal')
|
|
23526
|
+
) {
|
|
23527
|
+
// Unknown computed references may extract a mutator or sit under a
|
|
23528
|
+
// TypeScript/optional-chain wrapper before invocation. Decline the new
|
|
23529
|
+
// intrinsic-local proof even for unrelated dynamic property reads;
|
|
23530
|
+
// this is rejection only, not built-in name admission.
|
|
23531
|
+
intrinsicMutationReference = true;
|
|
23532
|
+
}
|
|
23533
|
+
}
|
|
23534
|
+
if (
|
|
23535
|
+
node.type === 'VariableDeclarator' &&
|
|
23536
|
+
parent?.type === 'VariableDeclaration' &&
|
|
23537
|
+
parent.kind === 'const' &&
|
|
23538
|
+
node.id?.type === 'Identifier' &&
|
|
23539
|
+
node.init
|
|
23540
|
+
) {
|
|
23541
|
+
intrinsicAliases.push(node);
|
|
23542
|
+
}
|
|
23543
|
+
if (node.type === 'MemberExpression' && isPossibleIntrinsicMutator(node)) {
|
|
23544
|
+
intrinsicMutationReferences.push([node.object, node, parent]);
|
|
23545
|
+
} else if (node.type === 'VariableDeclarator' && node.id?.type === 'ObjectPattern') {
|
|
23546
|
+
for (const property of node.id.properties || []) {
|
|
23547
|
+
if (property.type === 'Property' && isPossibleIntrinsicMutator(property)) {
|
|
23548
|
+
intrinsicMutationReferences.push([node.init, property]);
|
|
23549
|
+
}
|
|
23550
|
+
}
|
|
23551
|
+
}
|
|
23424
23552
|
if (
|
|
23425
23553
|
node.type === 'CallExpression' &&
|
|
23426
|
-
node.optional !== true &&
|
|
23427
23554
|
node.callee?.type === 'Identifier' &&
|
|
23428
23555
|
TEXT_INTRINSICS.has(node.callee.name)
|
|
23429
23556
|
) {
|
|
@@ -23469,7 +23596,8 @@ function applyStringChildProofs(ast, source, filename, facts) {
|
|
|
23469
23596
|
let lexical = null;
|
|
23470
23597
|
if (
|
|
23471
23598
|
intrinsicCalls.length > 0 ||
|
|
23472
|
-
((stringProofs.size > 0 || primitiveProofs.size > 0) &&
|
|
23599
|
+
((stringProofs.size > 0 || primitiveProofs.size > 0) &&
|
|
23600
|
+
(writes.length > 0 || intrinsicMutationReferences.length > 0))
|
|
23473
23601
|
) {
|
|
23474
23602
|
// The existing scope analysis understands var hoisting, parameter defaults,
|
|
23475
23603
|
// imports, catch bindings, and TSRX @for/@try scopes. Pay for it only when
|
|
@@ -23485,17 +23613,206 @@ function applyStringChildProofs(ast, source, filename, facts) {
|
|
|
23485
23613
|
}
|
|
23486
23614
|
}
|
|
23487
23615
|
if (lexical !== null) {
|
|
23616
|
+
const aliases = new WeakMap();
|
|
23617
|
+
for (const declaration of intrinsicAliases) {
|
|
23618
|
+
const scope = lexical.resolveBinding(
|
|
23619
|
+
lexical.nodeScopes.get(declaration.id),
|
|
23620
|
+
declaration.id.name,
|
|
23621
|
+
)?.scope;
|
|
23622
|
+
if (scope === undefined) continue;
|
|
23623
|
+
let values = aliases.get(scope);
|
|
23624
|
+
if (values === undefined) aliases.set(scope, (values = new Map()));
|
|
23625
|
+
values.set(declaration.id.name, declaration.init);
|
|
23626
|
+
}
|
|
23627
|
+
const initializer = (node) => {
|
|
23628
|
+
const scope = lexical.resolveBinding(lexical.nodeScopes.get(node), node.name)?.scope;
|
|
23629
|
+
return scope === undefined ? undefined : aliases.get(scope)?.get(node.name);
|
|
23630
|
+
};
|
|
23631
|
+
const intrinsicMemberName = (node) => {
|
|
23632
|
+
const property = node.computed ? unwrapTsExpr(node.property) : node.property;
|
|
23633
|
+
return node.computed
|
|
23634
|
+
? property?.type === 'Literal' && typeof property.value === 'string'
|
|
23635
|
+
? property.value
|
|
23636
|
+
: null
|
|
23637
|
+
: property?.type === 'Identifier'
|
|
23638
|
+
? property.name
|
|
23639
|
+
: null;
|
|
23640
|
+
};
|
|
23641
|
+
const globalOrigin = (expression, seen = new Set(), constructors = false) => {
|
|
23642
|
+
const node = unwrapTsExpr(expression);
|
|
23643
|
+
if (!node || seen.has(node)) return null;
|
|
23644
|
+
seen.add(node);
|
|
23645
|
+
if (node.type === 'Identifier') {
|
|
23646
|
+
const scope = lexical.nodeScopes.get(node);
|
|
23647
|
+
if (scope === undefined) return null;
|
|
23648
|
+
if (!lexical.isBound(scope, node.name)) {
|
|
23649
|
+
return INLINE_INTRINSIC_MUTATION_GLOBALS.has(node.name) ||
|
|
23650
|
+
(constructors && TEXT_INTRINSICS.has(node.name))
|
|
23651
|
+
? node.name
|
|
23652
|
+
: null;
|
|
23653
|
+
}
|
|
23654
|
+
return globalOrigin(initializer(node), seen, constructors);
|
|
23655
|
+
}
|
|
23656
|
+
if (node.type === 'ChainExpression') return globalOrigin(node.expression, seen, constructors);
|
|
23657
|
+
if (node.type === 'MemberExpression') {
|
|
23658
|
+
const name = intrinsicMemberName(node);
|
|
23659
|
+
if (name !== 'Object' && name !== 'Reflect' && !(constructors && TEXT_INTRINSICS.has(name)))
|
|
23660
|
+
return null;
|
|
23661
|
+
const origin = globalOrigin(node.object, seen);
|
|
23662
|
+
return origin !== null && INLINE_INTRINSIC_GLOBAL_RECEIVERS.has(origin) ? name : null;
|
|
23663
|
+
}
|
|
23664
|
+
return null;
|
|
23665
|
+
};
|
|
23666
|
+
const freshTarget = (expression, seen = new Set()) => {
|
|
23667
|
+
const node = unwrapTsExpr(expression);
|
|
23668
|
+
if (!node || seen.has(node)) return false;
|
|
23669
|
+
seen.add(node);
|
|
23670
|
+
if (node.type === 'ObjectExpression' || node.type === 'ArrayExpression') return true;
|
|
23671
|
+
return node.type === 'Identifier' && freshTarget(initializer(node), seen);
|
|
23672
|
+
};
|
|
23673
|
+
const visibleMutation =
|
|
23674
|
+
intrinsicMutationReferences.some(([receiver, reference, parent]) => {
|
|
23675
|
+
const origin = globalOrigin(receiver);
|
|
23676
|
+
const key = reference.type === 'Property' ? reference.key : reference.property;
|
|
23677
|
+
const property = reference.computed ? unwrapTsExpr(key) : key;
|
|
23678
|
+
const method = reference.computed ? property?.value : (property?.name ?? property?.value);
|
|
23679
|
+
if (origin === 'Object' || origin === 'Reflect') {
|
|
23680
|
+
// A normal native call targeting a fresh literal cannot directly write
|
|
23681
|
+
// a global constructor. Unknown getter/callback effects retain the
|
|
23682
|
+
// existing builtin assumptions; wrapped/extracted calls fail closed.
|
|
23683
|
+
const target = parent?.arguments?.[0];
|
|
23684
|
+
return !(
|
|
23685
|
+
parent?.type === 'CallExpression' &&
|
|
23686
|
+
parent.optional !== true &&
|
|
23687
|
+
parent.callee === reference &&
|
|
23688
|
+
reference.optional !== true &&
|
|
23689
|
+
INLINE_INTRINSIC_MUTATION_METHODS.has(method) &&
|
|
23690
|
+
freshTarget(target)
|
|
23691
|
+
);
|
|
23692
|
+
}
|
|
23693
|
+
return (
|
|
23694
|
+
origin !== null &&
|
|
23695
|
+
INLINE_INTRINSIC_GLOBAL_RECEIVERS.has(origin) &&
|
|
23696
|
+
(method === '__defineGetter__' ||
|
|
23697
|
+
method === '__defineSetter__' ||
|
|
23698
|
+
(reference.computed && property?.type !== 'Literal'))
|
|
23699
|
+
);
|
|
23700
|
+
}) ||
|
|
23701
|
+
writes.some((target) => {
|
|
23702
|
+
const node = unwrapTsExpr(target);
|
|
23703
|
+
if (node?.type !== 'MemberExpression') return false;
|
|
23704
|
+
const name = node.computed ? node.property?.value : node.property?.name;
|
|
23705
|
+
if (!TEXT_INTRINSICS.has(name) && !(node.computed && node.property?.type !== 'Literal'))
|
|
23706
|
+
return false;
|
|
23707
|
+
const origin = globalOrigin(node.object);
|
|
23708
|
+
return origin !== null && INLINE_INTRINSIC_GLOBAL_RECEIVERS.has(origin);
|
|
23709
|
+
});
|
|
23710
|
+
const unsafeCalls = new WeakSet();
|
|
23488
23711
|
for (const call of intrinsicCalls) {
|
|
23489
23712
|
const name = call.callee.name;
|
|
23490
23713
|
if (ambientIntrinsicValues.has(name)) continue;
|
|
23491
23714
|
const scope = lexical.nodeScopes.get(call);
|
|
23492
23715
|
if (scope !== undefined && !lexical.isBound(scope, name)) {
|
|
23716
|
+
if (visibleMutation) {
|
|
23717
|
+
unsafeCalls.add(call);
|
|
23718
|
+
continue;
|
|
23719
|
+
}
|
|
23720
|
+
if (call.optional === true) continue;
|
|
23493
23721
|
if (name === 'String' || name === 'Date') stringProofs.add(call);
|
|
23494
23722
|
else primitiveProofs.add(call);
|
|
23495
23723
|
}
|
|
23496
23724
|
}
|
|
23725
|
+
if (visibleMutation) {
|
|
23726
|
+
// TypeScript still describes the built-in return type after an asserted
|
|
23727
|
+
// replacement. Drop only supplied proofs that depend on these calls;
|
|
23728
|
+
// unrelated typed scalars and JavaScript's own coercion guarantees survive.
|
|
23729
|
+
const unsafeProof = (node, seen = new WeakSet()) => {
|
|
23730
|
+
if (!node || typeof node !== 'object' || seen.has(node)) return false;
|
|
23731
|
+
seen.add(node);
|
|
23732
|
+
if (unsafeCalls.has(node)) return true;
|
|
23733
|
+
if (node.type === 'CallExpression') {
|
|
23734
|
+
let callee = unwrapTsExpr(node.callee);
|
|
23735
|
+
while (callee?.type === 'ChainExpression') callee = unwrapTsExpr(callee.expression);
|
|
23736
|
+
if (callee?.type === 'MemberExpression') {
|
|
23737
|
+
const method = intrinsicMemberName(callee);
|
|
23738
|
+
if (method === 'call' || method === 'apply') callee = callee.object;
|
|
23739
|
+
}
|
|
23740
|
+
// Supplied facts may describe a saved constructor or its Function
|
|
23741
|
+
// call/apply result. Invalidation grants no new primitive admission.
|
|
23742
|
+
if (TEXT_INTRINSICS.has(globalOrigin(callee, new Set(), true))) return true;
|
|
23743
|
+
}
|
|
23744
|
+
if (node.type === 'Identifier' && unsafeProof(initializer(node), seen)) return true;
|
|
23745
|
+
if (Array.isArray(node)) return node.some((child) => unsafeProof(child, seen));
|
|
23746
|
+
for (const key in node) {
|
|
23747
|
+
if (AST_WALK_SKIP_KEYS.has(key)) continue;
|
|
23748
|
+
if (unsafeProof(node[key], seen)) return true;
|
|
23749
|
+
}
|
|
23750
|
+
return false;
|
|
23751
|
+
};
|
|
23752
|
+
for (const proofs of [stringProofs, primitiveProofs]) {
|
|
23753
|
+
for (const proof of proofs) if (unsafeProof(proof)) proofs.delete(proof);
|
|
23754
|
+
}
|
|
23755
|
+
}
|
|
23497
23756
|
}
|
|
23498
|
-
|
|
23757
|
+
const valueProofs = new Set();
|
|
23758
|
+
if (
|
|
23759
|
+
constantDeclarations.length > 0 &&
|
|
23760
|
+
(stringProofs.size > 0 ||
|
|
23761
|
+
primitiveProofs.size > 0 ||
|
|
23762
|
+
constantDeclarations.some((declaration) => isPrimitiveValueExpression(declaration.init)))
|
|
23763
|
+
) {
|
|
23764
|
+
lexical ??= createLexicalAnalysis(ast);
|
|
23765
|
+
// A local alias can mutate a global constructor without spelling its
|
|
23766
|
+
// global receiver. This check only declines new local intrinsic facts;
|
|
23767
|
+
// operator/template primitives and existing child proofs remain.
|
|
23768
|
+
const intrinsicLocalResultsSafe =
|
|
23769
|
+
!intrinsicMutationReference && !writes.some(mayWriteTextIntrinsicMember);
|
|
23770
|
+
const reassigned = collectReassignedBindings(ast);
|
|
23771
|
+
const declarationsByScope = new Map();
|
|
23772
|
+
for (const declaration of constantDeclarations) {
|
|
23773
|
+
if (reassigned.has(declaration.id)) continue;
|
|
23774
|
+
const scope = lexical.nodeScopes.get(declaration.id);
|
|
23775
|
+
if (scope === undefined) continue;
|
|
23776
|
+
const binding = lexical.resolveBinding(scope, declaration.id.name);
|
|
23777
|
+
if (binding === null) continue;
|
|
23778
|
+
let declarations = declarationsByScope.get(binding.scope);
|
|
23779
|
+
if (declarations === undefined) {
|
|
23780
|
+
declarations = new Map();
|
|
23781
|
+
declarationsByScope.set(binding.scope, declarations);
|
|
23782
|
+
}
|
|
23783
|
+
declarations.set(declaration.id.name, declaration);
|
|
23784
|
+
}
|
|
23785
|
+
const proven = new Map();
|
|
23786
|
+
const visiting = new Set();
|
|
23787
|
+
const primitive = (node) => isPrimitiveValueExpression(node, proveLocal);
|
|
23788
|
+
const proveLocal = (node) => {
|
|
23789
|
+
if (intrinsicLocalResultsSafe && (stringProofs.has(node) || primitiveProofs.has(node))) {
|
|
23790
|
+
return true;
|
|
23791
|
+
}
|
|
23792
|
+
if (node.type === 'Identifier') {
|
|
23793
|
+
const scope = lexical.nodeScopes.get(node);
|
|
23794
|
+
const binding = scope === undefined ? null : lexical.resolveBinding(scope, node.name);
|
|
23795
|
+
const declaration = declarationsByScope.get(binding?.scope)?.get(node.name);
|
|
23796
|
+
if (declaration === undefined || visiting.has(declaration)) return false;
|
|
23797
|
+
if (proven.has(declaration)) return proven.get(declaration);
|
|
23798
|
+
visiting.add(declaration);
|
|
23799
|
+
const result = primitive(declaration.init);
|
|
23800
|
+
visiting.delete(declaration);
|
|
23801
|
+
proven.set(declaration, result);
|
|
23802
|
+
return result;
|
|
23803
|
+
}
|
|
23804
|
+
return false;
|
|
23805
|
+
};
|
|
23806
|
+
// A value proof follows the lexical binding and its immutable initializer,
|
|
23807
|
+
// never a TS annotation or a renderer's authored text assertion. Reads and
|
|
23808
|
+
// throws still occur in setup; primitive results can contain native reads.
|
|
23809
|
+
for (const { node, parent, key } of references) {
|
|
23810
|
+
if (isIdentifierReference(node, parent, key, lexical) && primitive(node)) {
|
|
23811
|
+
valueProofs.add(node);
|
|
23812
|
+
}
|
|
23813
|
+
}
|
|
23814
|
+
}
|
|
23815
|
+
if (stringProofs.size === 0 && primitiveProofs.size === 0 && valueProofs.size === 0) return ast;
|
|
23499
23816
|
const rewritten = new WeakMap();
|
|
23500
23817
|
const rewrite = (node) => {
|
|
23501
23818
|
if (node === null || typeof node !== 'object') return node;
|
|
@@ -23521,7 +23838,18 @@ function applyStringChildProofs(ast, source, filename, facts) {
|
|
|
23521
23838
|
out[key] = next;
|
|
23522
23839
|
}
|
|
23523
23840
|
}
|
|
23524
|
-
if (
|
|
23841
|
+
if (valueProofs.has(node)) {
|
|
23842
|
+
out = {
|
|
23843
|
+
...out,
|
|
23844
|
+
metadata: {
|
|
23845
|
+
...out.metadata,
|
|
23846
|
+
octane_primitive_value: true,
|
|
23847
|
+
octane_primitive_local_value: true,
|
|
23848
|
+
...(stringProofs.has(node) ? { octane_string_child: true } : null),
|
|
23849
|
+
...(primitiveProofs.has(node) ? { octane_primitive_text_child: true } : null),
|
|
23850
|
+
},
|
|
23851
|
+
};
|
|
23852
|
+
} else if (stringProofs.has(node)) {
|
|
23525
23853
|
out = { ...out, metadata: { ...out.metadata, octane_string_child: true } };
|
|
23526
23854
|
} else if (primitiveProofs.has(node)) {
|
|
23527
23855
|
out = { ...out, metadata: { ...out.metadata, octane_primitive_text_child: true } };
|
|
@@ -23534,20 +23862,73 @@ function applyStringChildProofs(ast, source, filename, facts) {
|
|
|
23534
23862
|
}
|
|
23535
23863
|
|
|
23536
23864
|
const TEXT_INTRINSICS = new Set(['String', 'Number', 'BigInt', 'Date']);
|
|
23865
|
+
const INTRINSIC_MUTATION_METHODS = new Set([
|
|
23866
|
+
'assign',
|
|
23867
|
+
'defineProperty',
|
|
23868
|
+
'defineProperties',
|
|
23869
|
+
'set',
|
|
23870
|
+
'__defineGetter__',
|
|
23871
|
+
'__defineSetter__',
|
|
23872
|
+
'setPrototypeOf',
|
|
23873
|
+
'deleteProperty',
|
|
23874
|
+
]);
|
|
23875
|
+
|
|
23876
|
+
function mayWriteTextIntrinsicMember(target) {
|
|
23877
|
+
target = unwrapTsExpr(target);
|
|
23878
|
+
if (!target || typeof target !== 'object') return false;
|
|
23879
|
+
if (target.type === 'AssignmentPattern') return mayWriteTextIntrinsicMember(target.left);
|
|
23880
|
+
if (target.type === 'MemberExpression') {
|
|
23881
|
+
return target.computed
|
|
23882
|
+
? target.property?.type !== 'Literal' || TEXT_INTRINSICS.has(target.property.value)
|
|
23883
|
+
: TEXT_INTRINSICS.has(target.property?.name);
|
|
23884
|
+
}
|
|
23885
|
+
if (target.type === 'RestElement') return mayWriteTextIntrinsicMember(target.argument);
|
|
23886
|
+
if (target.type === 'ArrayPattern') {
|
|
23887
|
+
return (target.elements || []).some(mayWriteTextIntrinsicMember);
|
|
23888
|
+
}
|
|
23889
|
+
if (target.type === 'ObjectPattern') {
|
|
23890
|
+
return (target.properties || []).some((property) =>
|
|
23891
|
+
mayWriteTextIntrinsicMember(property.argument ?? property.value),
|
|
23892
|
+
);
|
|
23893
|
+
}
|
|
23894
|
+
return false;
|
|
23895
|
+
}
|
|
23896
|
+
|
|
23897
|
+
const INLINE_INTRINSIC_MUTATION_METHODS = new Set([
|
|
23898
|
+
'assign',
|
|
23899
|
+
'defineProperty',
|
|
23900
|
+
'defineProperties',
|
|
23901
|
+
'set',
|
|
23902
|
+
'__defineGetter__',
|
|
23903
|
+
'__defineSetter__',
|
|
23904
|
+
'setPrototypeOf',
|
|
23905
|
+
'deleteProperty',
|
|
23906
|
+
]);
|
|
23907
|
+
const INLINE_INTRINSIC_GLOBAL_RECEIVERS = new Set(['globalThis', 'window', 'self', 'global']);
|
|
23908
|
+
const INLINE_INTRINSIC_MUTATION_GLOBALS = new Set([
|
|
23909
|
+
'Object',
|
|
23910
|
+
'Reflect',
|
|
23911
|
+
...INLINE_INTRINSIC_GLOBAL_RECEIVERS,
|
|
23912
|
+
]);
|
|
23913
|
+
|
|
23914
|
+
// Inspect the reference itself: TS/optional wrappers and extracted methods may
|
|
23915
|
+
// hide its eventual call. Lexical receiver checks keep ordinary computed reads
|
|
23916
|
+
// and application .set methods from invalidating intrinsic-result proofs.
|
|
23917
|
+
function isPossibleIntrinsicMutator(node) {
|
|
23918
|
+
const key = node.type === 'Property' ? node.key : node.property;
|
|
23919
|
+
const property = node.computed ? unwrapTsExpr(key) : key;
|
|
23920
|
+
const name = node.computed ? property?.value : (property?.name ?? property?.value);
|
|
23921
|
+
return (
|
|
23922
|
+
INLINE_INTRINSIC_MUTATION_METHODS.has(name) || (node.computed && property?.type !== 'Literal')
|
|
23923
|
+
);
|
|
23924
|
+
}
|
|
23537
23925
|
|
|
23538
23926
|
// A visible replacement of a global constructor invalidates inferred text
|
|
23539
23927
|
// proofs for the module. Calls still evaluate the authored callee: a replacement
|
|
23540
23928
|
// may return an element rather than the built-in's primitive result.
|
|
23541
23929
|
function writesGlobalTextIntrinsic(target, lexical) {
|
|
23930
|
+
target = unwrapTsExpr(target);
|
|
23542
23931
|
if (!target || typeof target !== 'object') return false;
|
|
23543
|
-
if (
|
|
23544
|
-
target.type === 'TSAsExpression' ||
|
|
23545
|
-
target.type === 'TSTypeAssertion' ||
|
|
23546
|
-
target.type === 'TSNonNullExpression' ||
|
|
23547
|
-
target.type === 'ParenthesizedExpression'
|
|
23548
|
-
) {
|
|
23549
|
-
return writesGlobalTextIntrinsic(target.expression, lexical);
|
|
23550
|
-
}
|
|
23551
23932
|
const unbound = (node, name) => {
|
|
23552
23933
|
const scope = lexical.nodeScopes.get(node);
|
|
23553
23934
|
return scope !== undefined && !lexical.isBound(scope, name);
|
|
@@ -25113,6 +25494,69 @@ function planJsx(
|
|
|
25113
25494
|
if (b.deferred) everyRenderLines.push(updateEmit);
|
|
25114
25495
|
else updateLines.push(updateEmit);
|
|
25115
25496
|
}
|
|
25497
|
+
if (ctx.scalarBindingClaims) {
|
|
25498
|
+
// Only opted-in scalar views can retain another writer's publication at
|
|
25499
|
+
// hydration. Record their existing cache fields; ordinary output is unchanged.
|
|
25500
|
+
const fields = [];
|
|
25501
|
+
for (const binding of elementBindings) {
|
|
25502
|
+
const key = binding.id;
|
|
25503
|
+
const text = binding.kind === 'textOnlyChild' || binding.kind === 'text';
|
|
25504
|
+
const host = text && !binding.signalDirect ? `_txt$${key}` : `_el$${key}`;
|
|
25505
|
+
if (text && binding.signalDirect)
|
|
25506
|
+
fields.push(bag.letter(`_sig$${key}`), bag.letter(host), '#text-token');
|
|
25507
|
+
if (binding.kind === 'nativeStyle') {
|
|
25508
|
+
fields.push(binding.slotIndex, bag.letter(host), 'style');
|
|
25509
|
+
} else if (binding.kind === 'styleProperties') {
|
|
25510
|
+
for (let i = 0; i < binding.properties.length; i++)
|
|
25511
|
+
fields.push(
|
|
25512
|
+
bag.letter(`_prev$${key}_${i}`),
|
|
25513
|
+
bag.letter(host),
|
|
25514
|
+
`style:${binding.properties[i].name}`,
|
|
25515
|
+
);
|
|
25516
|
+
} else if (binding.kind === 'style') {
|
|
25517
|
+
fields.push(bag.letter(`_sty$${key}`), bag.letter(host), 'style');
|
|
25518
|
+
} else if (
|
|
25519
|
+
text ||
|
|
25520
|
+
['attr', 'stringData', 'booleanAttr', 'ariaAttr', 'class', 'styleProperty'].includes(
|
|
25521
|
+
binding.kind,
|
|
25522
|
+
)
|
|
25523
|
+
) {
|
|
25524
|
+
const cache = binding.signalDirect && !text ? `_sig$${key}` : `_prev$${key}`;
|
|
25525
|
+
fields.push(
|
|
25526
|
+
bag.letter(cache),
|
|
25527
|
+
bag.letter(host),
|
|
25528
|
+
text
|
|
25529
|
+
? '#text'
|
|
25530
|
+
: binding.kind === 'styleProperty'
|
|
25531
|
+
? `style:${binding.name}`
|
|
25532
|
+
: binding.kind === 'class'
|
|
25533
|
+
? 'class'
|
|
25534
|
+
: ATTRIBUTE_ALIASES.get(binding.name) || binding.name,
|
|
25535
|
+
);
|
|
25536
|
+
}
|
|
25537
|
+
}
|
|
25538
|
+
if (fields.length) {
|
|
25539
|
+
const name = `_claims$${ctx.nextHelperId++}`;
|
|
25540
|
+
ctx.hoistedHelpers.push(
|
|
25541
|
+
inheritOriginLoc(
|
|
25542
|
+
b.const(name, b.array(fields.map((value) => b.literal(value)))),
|
|
25543
|
+
planOrigin,
|
|
25544
|
+
),
|
|
25545
|
+
);
|
|
25546
|
+
everyRenderLines.push(
|
|
25547
|
+
inheritOriginLoc(
|
|
25548
|
+
b.stmt(
|
|
25549
|
+
b.call(
|
|
25550
|
+
requireRuntimeForContext(ctx, 'hydrateClaimedBindingCaches'),
|
|
25551
|
+
b.id('__s'),
|
|
25552
|
+
b.id(name),
|
|
25553
|
+
),
|
|
25554
|
+
),
|
|
25555
|
+
planOrigin,
|
|
25556
|
+
),
|
|
25557
|
+
);
|
|
25558
|
+
}
|
|
25559
|
+
}
|
|
25116
25560
|
if (keyedSelection !== null && single && !noTemplate) {
|
|
25117
25561
|
// Reuse the ordinary root-class write and its exact bag fields. Updating
|
|
25118
25562
|
// only the DOM would leave the normal row diff's previous value stale.
|
|
@@ -28113,11 +28557,13 @@ function emitElementHtml(
|
|
|
28113
28557
|
appendTemplatePart(attrTemplate, ` data-octane-input="${escapeAttr(site)}"`, 'attribute', null);
|
|
28114
28558
|
};
|
|
28115
28559
|
const markDirectControl = (binding, expression, origin) => {
|
|
28116
|
-
|
|
28560
|
+
const carriesHandle = canCarryDirectSignalHandle(expression);
|
|
28561
|
+
if (!carriesHandle && !canCarryDirectSignalHandle(expression, false, true)) return binding;
|
|
28117
28562
|
const site = directSignalSite(ctx, node, 'input');
|
|
28118
28563
|
ensureDirectControlSite(site);
|
|
28119
28564
|
ctx.signalBindingsUsed = true;
|
|
28120
28565
|
if (isDirectSignalHandleExpression(expression)) ctx.signalBindingsEager = true;
|
|
28566
|
+
if (!carriesHandle) return binding;
|
|
28121
28567
|
return { ...binding, signalDirect: true, signalSite: site };
|
|
28122
28568
|
};
|
|
28123
28569
|
const hostSignalSite = directSignalSite(ctx, node, 'binding');
|
|
@@ -30312,6 +30758,59 @@ function collectAutoMemoDependencyExpressions(nodes) {
|
|
|
30312
30758
|
};
|
|
30313
30759
|
}
|
|
30314
30760
|
|
|
30761
|
+
function requireCompiledHydrateAlias(ctx) {
|
|
30762
|
+
const helper = '__HydrateCompiled';
|
|
30763
|
+
let alias = ctx.privateRuntimeAliases?.get(helper);
|
|
30764
|
+
if (alias === undefined) {
|
|
30765
|
+
alias = allocCompilerName(ctx, rtAlias(helper));
|
|
30766
|
+
(ctx.privateRuntimeAliases ??= new Map()).set(helper, alias);
|
|
30767
|
+
}
|
|
30768
|
+
ctx.runtimeNeeded.add(helper);
|
|
30769
|
+
return alias;
|
|
30770
|
+
}
|
|
30771
|
+
|
|
30772
|
+
function isPrivateSplitContextProvider(node, ctx) {
|
|
30773
|
+
const tag = node.openingElement?.name ?? node.id;
|
|
30774
|
+
const binding = ctx.privateSplitContextProviders?.get(tag);
|
|
30775
|
+
if (binding === undefined || tag.type !== 'JSXIdentifier' || tag.name !== binding.name)
|
|
30776
|
+
return false;
|
|
30777
|
+
const lexical = (ctx.activityLexical ??= createLexicalAnalysis(ctx.activityModuleAst));
|
|
30778
|
+
if (!lexical.bindingNodes.has(binding)) return false;
|
|
30779
|
+
const owner = lexical.resolveBinding(lexical.nodeScopes.get(binding), binding.name);
|
|
30780
|
+
return (
|
|
30781
|
+
owner !== null &&
|
|
30782
|
+
lexical.resolveBinding(lexical.nodeScopes.get(tag), tag.name)?.scope === owner.scope
|
|
30783
|
+
);
|
|
30784
|
+
}
|
|
30785
|
+
|
|
30786
|
+
function isCompiledHydrateTemplate(node, ctx, attrs) {
|
|
30787
|
+
if (!ctx.compiledHydrateTemplates || ctx._universalRuntimeUnit != null) return false;
|
|
30788
|
+
const tag = node.openingElement?.name ?? node.id;
|
|
30789
|
+
if (
|
|
30790
|
+
(tag?.type !== 'Identifier' && tag?.type !== 'JSXIdentifier') ||
|
|
30791
|
+
ctx.octaneImportLocals?.get(tag.name) !== 'Hydrate'
|
|
30792
|
+
)
|
|
30793
|
+
return false;
|
|
30794
|
+
if (!ctx.compiledSplitHydrateTags?.has(tag)) {
|
|
30795
|
+
for (const attr of attrs) {
|
|
30796
|
+
if (attr.type !== 'Attribute' && attr.type !== 'JSXAttribute') return false;
|
|
30797
|
+
const name = attr.name?.name ?? attr.name;
|
|
30798
|
+
if (
|
|
30799
|
+
typeof name !== 'string' ||
|
|
30800
|
+
name === 'children' ||
|
|
30801
|
+
name === 'fallback' ||
|
|
30802
|
+
name.startsWith('__')
|
|
30803
|
+
)
|
|
30804
|
+
return false;
|
|
30805
|
+
}
|
|
30806
|
+
}
|
|
30807
|
+
const lexical = (ctx.activityLexical ??= createLexicalAnalysis(ctx.activityModuleAst));
|
|
30808
|
+
const scope = lexical.nodeScopes.get(tag);
|
|
30809
|
+
if (scope === undefined) return false;
|
|
30810
|
+
const binding = lexical.resolveBinding(scope, tag.name);
|
|
30811
|
+
return binding?.scope === lexical.rootScope && binding.importSource?.value === 'octane';
|
|
30812
|
+
}
|
|
30813
|
+
|
|
30315
30814
|
function makeCompCall(
|
|
30316
30815
|
node,
|
|
30317
30816
|
ctx,
|
|
@@ -30330,7 +30829,7 @@ function makeCompCall(
|
|
|
30330
30829
|
const staticFragmentRenderer = node.openingElement?.metadata?.staticFragmentRenderer;
|
|
30331
30830
|
const activityDescriptor = isActivityLongForm(node, ctx);
|
|
30332
30831
|
const descriptorConfig = activityDescriptor || hasKeyedSpreadConfig(node);
|
|
30333
|
-
|
|
30832
|
+
let compNode = activityDescriptor
|
|
30334
30833
|
? inheritOriginLoc(b.id(requireRuntimeForContext(ctx, 'Activity')), node)
|
|
30335
30834
|
: staticFragmentRenderer
|
|
30336
30835
|
? inheritOriginLoc(b.id(staticFragmentRenderer.name), node.openingElement?.name || node.id)
|
|
@@ -30403,6 +30902,7 @@ function makeCompCall(
|
|
|
30403
30902
|
// arrow while making its body printable. Whitespace-only JSXText around the
|
|
30404
30903
|
// arrow is ignored so source indentation doesn't defeat the detection.
|
|
30405
30904
|
const sourceChildren = node.children || [];
|
|
30905
|
+
let compiledChildren = false;
|
|
30406
30906
|
const renderPropChild = soleRenderPropChild(sourceChildren);
|
|
30407
30907
|
if (renderPropChild) {
|
|
30408
30908
|
hasChildrenProp = true;
|
|
@@ -30434,6 +30934,7 @@ function makeCompCall(
|
|
|
30434
30934
|
propNodes.push(b.prop('init', b.literal('children'), childrenValue));
|
|
30435
30935
|
}
|
|
30436
30936
|
} else {
|
|
30937
|
+
compiledChildren = true;
|
|
30437
30938
|
// Compile children as a render function: (scope) => { renders JSX into scope }.
|
|
30438
30939
|
// The function is inlined inside the parent component body so its closures
|
|
30439
30940
|
// capture the parent's locals (props, state, etc.).
|
|
@@ -30524,7 +31025,16 @@ function makeCompCall(
|
|
|
30524
31025
|
// elides iff the callee carries the definition-site `$$singleRoot` stamp
|
|
30525
31026
|
// (docs/comment-marker-elision-plan.md M1).
|
|
30526
31027
|
let maybeSingleRoot = false;
|
|
30527
|
-
if (
|
|
31028
|
+
if (
|
|
31029
|
+
(compiledChildren || ctx.compiledSplitHydrateTags?.has(node.openingElement?.name ?? node.id)) &&
|
|
31030
|
+
isCompiledHydrateTemplate(node, ctx, attrs)
|
|
31031
|
+
) {
|
|
31032
|
+
compNode = inheritOriginLoc(
|
|
31033
|
+
b.id(requireCompiledHydrateAlias(ctx)),
|
|
31034
|
+
node.openingElement?.name ?? node.id,
|
|
31035
|
+
);
|
|
31036
|
+
voidComponent = true;
|
|
31037
|
+
} else if (staticFragmentRenderer) {
|
|
30528
31038
|
// The renderer is already a void, hookless component body. A memo boundary
|
|
30529
31039
|
// would force a full Block and could hide a hookful descendant's update.
|
|
30530
31040
|
liteEligible = true;
|
|
@@ -30638,6 +31148,11 @@ function makeCompCall(
|
|
|
30638
31148
|
maybeSingleRoot = callSiteOk;
|
|
30639
31149
|
}
|
|
30640
31150
|
const importedBinding = ctx.importedComponentBindings?.get(compName);
|
|
31151
|
+
if (
|
|
31152
|
+
!voidComponent &&
|
|
31153
|
+
(ctx.privateCompiledContexts?.has(compName) || isPrivateSplitContextProvider(node, ctx))
|
|
31154
|
+
)
|
|
31155
|
+
voidComponent = true;
|
|
30641
31156
|
if (
|
|
30642
31157
|
!voidComponent &&
|
|
30643
31158
|
!ctx.hmr &&
|