octane 0.2.13 → 0.2.14
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 +7 -7
- package/dist/cjs/dom-binding-handoff.cjs +2 -2
- package/dist/cjs/dom-bindings.cjs +70 -7
- package/dist/cjs/runtime.cjs +294 -53
- package/dist/cjs/server/streamed-signals.cjs +3 -2
- package/dist/cjs/signals/control-binding.cjs +33 -9
- package/dist/cjs/signals/control-handoff.cjs +39 -0
- package/dist/cjs/stream-protocol.cjs +6 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/compiler/compile.js +86 -24
- package/dist/compiler/dom-binding-program.js +18 -2
- package/dist/compiler/dom-bindings.js +87 -30
- package/dist/compiler/volar.js +58 -17
- package/dist/constants.d.ts +2 -2
- package/dist/constants.js +2 -2
- package/dist/dom-binding-handoff.d.ts +6 -4
- package/dist/dom-binding-handoff.js +1 -1
- package/dist/dom-binding-program.d.ts +2 -1
- package/dist/dom-binding-program.js +16 -1
- package/dist/dom-bindings.d.ts +2 -0
- package/dist/dom-bindings.js +70 -7
- package/dist/runtime.d.ts +34 -3
- package/dist/runtime.js +297 -53
- package/dist/server/streamed-signals.js +3 -2
- package/dist/signals/control-binding.d.ts +9 -5
- package/dist/signals/control-binding.js +38 -6
- package/dist/signals/control-handoff.d.ts +26 -0
- package/dist/signals/control-handoff.js +12 -0
- package/dist/signals/index.d.ts +1 -0
- package/dist/stream-protocol.d.ts +4 -0
- package/dist/stream-protocol.js +4 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -25,11 +25,11 @@ module.exports = __toCommonJS(control_binding_exports);
|
|
|
25
25
|
var import_control_capture = require("../hydration/control-capture.cjs");
|
|
26
26
|
var import_early_values = require("./early-values.cjs");
|
|
27
27
|
var import_handle_protocol = require("./handle-protocol.cjs");
|
|
28
|
+
var import_control_handoff = require("./control-handoff.cjs");
|
|
28
29
|
var import_owner_context = require("./owner-context.cjs");
|
|
29
30
|
var import_read_protocol = require("./read-protocol.cjs");
|
|
30
31
|
var import_types = require("./types.cjs");
|
|
31
32
|
var import_transition_state = require("./transition-state.cjs");
|
|
32
|
-
const CONTROL_BINDINGS = /* @__PURE__ */ new WeakMap();
|
|
33
33
|
const RADIO_WRITERS = /* @__PURE__ */ new WeakMap();
|
|
34
34
|
function controlString(value) {
|
|
35
35
|
return typeof value === "string" ? value : typeof value === "function" || typeof value === "symbol" ? "" : String(value);
|
|
@@ -48,8 +48,7 @@ function publishRadioInput(input) {
|
|
|
48
48
|
for (const [write, checked] of edits) if (!checked) write(false);
|
|
49
49
|
for (const [write, checked] of edits) if (checked) write(true);
|
|
50
50
|
}
|
|
51
|
-
function __createBindingControls() {
|
|
52
|
-
const owner = (0, import_owner_context.currentSignalOwner)();
|
|
51
|
+
function __createBindingControls(owner = (0, import_owner_context.currentSignalOwner)()) {
|
|
53
52
|
const run = owner === null ? (callback) => callback() : (0, import_owner_context.captureSignalOwner)(owner);
|
|
54
53
|
return {
|
|
55
54
|
claim(element, channel, notify) {
|
|
@@ -58,12 +57,12 @@ function __createBindingControls() {
|
|
|
58
57
|
throw new TypeError(
|
|
59
58
|
"A signal control requires a native value/checked property and a signal."
|
|
60
59
|
);
|
|
61
|
-
if (CONTROL_BINDINGS.get(control)?.has(channel) || (0, import_early_values.hasHydrationControlSignalWriter)(control, channel))
|
|
60
|
+
if (import_control_handoff.CONTROL_BINDINGS.get(control)?.has(channel) || (0, import_early_values.hasHydrationControlSignalWriter)(control, channel))
|
|
62
61
|
throw new Error(
|
|
63
62
|
"This control property already has a signal binding. Dispose it before rebinding."
|
|
64
63
|
);
|
|
65
|
-
let channels = CONTROL_BINDINGS.get(control);
|
|
66
|
-
if (!channels) CONTROL_BINDINGS.set(control, channels = /* @__PURE__ */ new Set());
|
|
64
|
+
let channels = import_control_handoff.CONTROL_BINDINGS.get(control);
|
|
65
|
+
if (!channels) import_control_handoff.CONTROL_BINDINGS.set(control, channels = /* @__PURE__ */ new Set());
|
|
67
66
|
channels.add(channel);
|
|
68
67
|
let disposed = false;
|
|
69
68
|
let initialized = false;
|
|
@@ -135,7 +134,7 @@ function __createBindingControls() {
|
|
|
135
134
|
control.removeEventListener("compositionstart", compositionStart);
|
|
136
135
|
control.removeEventListener("compositionend", compositionEnd);
|
|
137
136
|
channels.delete(channel);
|
|
138
|
-
if (channels.size === 0) CONTROL_BINDINGS.delete(control);
|
|
137
|
+
if (channels.size === 0) import_control_handoff.CONTROL_BINDINGS.delete(control);
|
|
139
138
|
if (failed) throw failure;
|
|
140
139
|
};
|
|
141
140
|
const prepare = (next, preview = false) => {
|
|
@@ -298,11 +297,16 @@ function __createBindingControls() {
|
|
|
298
297
|
prepare,
|
|
299
298
|
preview: (value) => prepare(value, true),
|
|
300
299
|
prepareCurrent: () => prepare(raw),
|
|
300
|
+
active: () => !disposed,
|
|
301
|
+
composing: () => composing,
|
|
301
302
|
dispose
|
|
302
303
|
};
|
|
303
304
|
}
|
|
304
305
|
};
|
|
305
306
|
}
|
|
307
|
+
function controlSignalOwner(owner, document) {
|
|
308
|
+
return owner !== null && "documentOwner" in owner ? document ? owner.documentOwner : owner.instanceOwner : owner;
|
|
309
|
+
}
|
|
306
310
|
function bindSignalControl(control, channel, handle$) {
|
|
307
311
|
if (!(0, import_handle_protocol.isSignalHandle)(handle$))
|
|
308
312
|
throw new TypeError("A signal control requires a native value/checked property and a signal.");
|
|
@@ -310,6 +314,7 @@ function bindSignalControl(control, channel, handle$) {
|
|
|
310
314
|
let dirty = false;
|
|
311
315
|
let disposed = false;
|
|
312
316
|
let lease;
|
|
317
|
+
let handoff;
|
|
313
318
|
const dispose = () => {
|
|
314
319
|
disposed = true;
|
|
315
320
|
lease.dispose();
|
|
@@ -357,9 +362,28 @@ function bindSignalControl(control, channel, handle$) {
|
|
|
357
362
|
};
|
|
358
363
|
}
|
|
359
364
|
};
|
|
360
|
-
|
|
365
|
+
const owner = (0, import_owner_context.currentSignalOwner)();
|
|
366
|
+
lease = __createBindingControls(owner).claim(control, channel, refresh);
|
|
361
367
|
refresh();
|
|
362
|
-
return dispose
|
|
368
|
+
return Object.assign(dispose, {
|
|
369
|
+
[import_control_handoff.CONTROL_HANDOFF]() {
|
|
370
|
+
return handoff ??= {
|
|
371
|
+
control,
|
|
372
|
+
channel,
|
|
373
|
+
active: () => !disposed && lease.active(),
|
|
374
|
+
matches(handle) {
|
|
375
|
+
if (disposed || !lease.active() || handle !== handle$) return false;
|
|
376
|
+
if (!(import_types.SIGNAL_OWNER_RESOLVE in handle$)) return true;
|
|
377
|
+
const document = handle$[import_types.SIGNAL_BINDING_IDENTITY]().scope === "document";
|
|
378
|
+
return owner !== null && controlSignalOwner(owner, document) === controlSignalOwner((0, import_owner_context.currentSignalOwner)(), document) && !disposed && lease.active();
|
|
379
|
+
},
|
|
380
|
+
composing: lease.composing,
|
|
381
|
+
// dispose revokes authority before invoking fallible subscription
|
|
382
|
+
// cleanup; a later call cannot release the renderer's replacement.
|
|
383
|
+
retire: dispose
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
});
|
|
363
387
|
}
|
|
364
388
|
// Annotate the CommonJS export names for ESM import in node:
|
|
365
389
|
0 && (module.exports = {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var control_handoff_exports = {};
|
|
20
|
+
__export(control_handoff_exports, {
|
|
21
|
+
BINDING_HANDOFF: () => BINDING_HANDOFF,
|
|
22
|
+
CONTROL_BINDINGS: () => CONTROL_BINDINGS,
|
|
23
|
+
CONTROL_HANDOFF: () => CONTROL_HANDOFF,
|
|
24
|
+
hasSignalControlBinding: () => hasSignalControlBinding
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(control_handoff_exports);
|
|
27
|
+
const BINDING_HANDOFF = /* @__PURE__ */ Symbol.for("octane.binding-handoff");
|
|
28
|
+
const CONTROL_HANDOFF = /* @__PURE__ */ Symbol.for("octane.control-handoff");
|
|
29
|
+
const CONTROL_BINDINGS = /* @__PURE__ */ new WeakMap();
|
|
30
|
+
function hasSignalControlBinding(control, channel) {
|
|
31
|
+
return CONTROL_BINDINGS.get(control)?.has(channel) ?? false;
|
|
32
|
+
}
|
|
33
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
34
|
+
0 && (module.exports = {
|
|
35
|
+
BINDING_HANDOFF,
|
|
36
|
+
CONTROL_BINDINGS,
|
|
37
|
+
CONTROL_HANDOFF,
|
|
38
|
+
hasSignalControlBinding
|
|
39
|
+
});
|
|
@@ -20,6 +20,8 @@ var stream_protocol_exports = {};
|
|
|
20
20
|
__export(stream_protocol_exports, {
|
|
21
21
|
HYDRATE_STREAM_TOKEN_ATTR: () => HYDRATE_STREAM_TOKEN_ATTR,
|
|
22
22
|
STREAM_BOUNDARY_ATTR: () => STREAM_BOUNDARY_ATTR,
|
|
23
|
+
STREAM_SCRIPT_ATTR: () => STREAM_SCRIPT_ATTR,
|
|
24
|
+
SUSPENSE_SCRIPT_ATTR: () => SUSPENSE_SCRIPT_ATTR,
|
|
23
25
|
isRendererStreamBoundaryTemplate: () => isRendererStreamBoundaryTemplate,
|
|
24
26
|
isRendererStreamToken: () => isRendererStreamToken,
|
|
25
27
|
rendererRangeClose: () => rendererRangeClose,
|
|
@@ -28,6 +30,8 @@ __export(stream_protocol_exports, {
|
|
|
28
30
|
module.exports = __toCommonJS(stream_protocol_exports);
|
|
29
31
|
var import_dom_binding_protocol = require("./dom-binding-protocol.cjs");
|
|
30
32
|
const STREAM_BOUNDARY_ATTR = "data-oct-b";
|
|
33
|
+
const SUSPENSE_SCRIPT_ATTR = "data-octane-suspense";
|
|
34
|
+
const STREAM_SCRIPT_ATTR = "data-octane-stream";
|
|
31
35
|
const HYDRATE_STREAM_TOKEN_ATTR = "data-octane-stream-token";
|
|
32
36
|
function hydrationMarkerMultiplicity(data, open) {
|
|
33
37
|
const marker = open ? "[" : "]";
|
|
@@ -93,6 +97,8 @@ function isRendererStreamBoundaryTemplate(node, expectedToken) {
|
|
|
93
97
|
0 && (module.exports = {
|
|
94
98
|
HYDRATE_STREAM_TOKEN_ATTR,
|
|
95
99
|
STREAM_BOUNDARY_ATTR,
|
|
100
|
+
STREAM_SCRIPT_ATTR,
|
|
101
|
+
SUSPENSE_SCRIPT_ATTR,
|
|
96
102
|
isRendererStreamBoundaryTemplate,
|
|
97
103
|
isRendererStreamToken,
|
|
98
104
|
rendererRangeClose,
|
package/dist/cjs/version.cjs
CHANGED
|
@@ -21,7 +21,7 @@ __export(version_exports, {
|
|
|
21
21
|
version: () => version
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(version_exports);
|
|
24
|
-
const version = "0.2.
|
|
24
|
+
const version = "0.2.14";
|
|
25
25
|
// Annotate the CommonJS export names for ESM import in node:
|
|
26
26
|
0 && (module.exports = {
|
|
27
27
|
version
|
package/dist/compiler/compile.js
CHANGED
|
@@ -9163,6 +9163,7 @@ function markKnownAttributeSpreads(ast, contracts) {
|
|
|
9163
9163
|
}
|
|
9164
9164
|
}
|
|
9165
9165
|
const imports = new Map();
|
|
9166
|
+
const unboundImports = new Set();
|
|
9166
9167
|
for (const declaration of ast.body) {
|
|
9167
9168
|
if (declaration.type !== 'ImportDeclaration' || declaration.importKind === 'type') continue;
|
|
9168
9169
|
for (const specifier of declaration.specifiers) {
|
|
@@ -9173,6 +9174,8 @@ function markKnownAttributeSpreads(ast, contracts) {
|
|
|
9173
9174
|
: specifier.type === 'ImportDefaultSpecifier'
|
|
9174
9175
|
? 'default'
|
|
9175
9176
|
: (specifier.imported.name ?? specifier.imported.value);
|
|
9177
|
+
if (declaration.source.value === 'octane/behavior' && imported === 'unbound')
|
|
9178
|
+
unboundImports.add(specifier.local.name);
|
|
9176
9179
|
const matches = contracts.filter(
|
|
9177
9180
|
(contract) =>
|
|
9178
9181
|
contract.source === declaration.source.value && contract.imported === imported,
|
|
@@ -9293,13 +9296,24 @@ function markKnownAttributeSpreads(ast, contracts) {
|
|
|
9293
9296
|
};
|
|
9294
9297
|
}
|
|
9295
9298
|
if (node.type !== 'JSXSpreadAttribute' && node.type !== 'SpreadAttribute') return null;
|
|
9296
|
-
|
|
9299
|
+
let call = unwrapTsExpr(node.argument);
|
|
9300
|
+
const externalCallee = unwrapTsExpr(call?.callee);
|
|
9301
|
+
const external =
|
|
9302
|
+
call?.type === 'CallExpression' &&
|
|
9303
|
+
!call.optional &&
|
|
9304
|
+
externalCallee?.type === 'Identifier' &&
|
|
9305
|
+
unboundImports.has(externalCallee.name) &&
|
|
9306
|
+
lexical.resolveBinding(lexical.nodeScopes.get(externalCallee), externalCallee.name)?.scope ===
|
|
9307
|
+
lexical.rootScope &&
|
|
9308
|
+
call.arguments.length === 1 &&
|
|
9309
|
+
call.arguments[0].type !== 'SpreadElement';
|
|
9310
|
+
if (external) call = unwrapTsExpr(call.arguments[0]);
|
|
9297
9311
|
if (call?.type !== 'CallExpression' || call.optional) return null;
|
|
9298
|
-
let callee = call.callee;
|
|
9312
|
+
let callee = unwrapTsExpr(call.callee);
|
|
9299
9313
|
const members = [];
|
|
9300
9314
|
while (callee?.type === 'MemberExpression' && !callee.computed && !callee.optional) {
|
|
9301
9315
|
members.unshift(callee.property.name);
|
|
9302
|
-
callee = callee.object;
|
|
9316
|
+
callee = unwrapTsExpr(callee.object);
|
|
9303
9317
|
}
|
|
9304
9318
|
if (callee?.type !== 'Identifier') return null;
|
|
9305
9319
|
const matches = imports.get(callee.name);
|
|
@@ -9319,7 +9333,11 @@ function markKnownAttributeSpreads(ast, contracts) {
|
|
|
9319
9333
|
return contract
|
|
9320
9334
|
? {
|
|
9321
9335
|
...node,
|
|
9322
|
-
_octaneKnownAttributeSpread: {
|
|
9336
|
+
_octaneKnownAttributeSpread: {
|
|
9337
|
+
fields: [...contract.fields],
|
|
9338
|
+
style: contract.style,
|
|
9339
|
+
...(external ? { unbound: true } : {}),
|
|
9340
|
+
},
|
|
9323
9341
|
}
|
|
9324
9342
|
: null;
|
|
9325
9343
|
});
|
|
@@ -15202,11 +15220,13 @@ function compileComponent(node, ctx, options) {
|
|
|
15202
15220
|
}
|
|
15203
15221
|
|
|
15204
15222
|
/** Gate explicitly eligible presentation views; ordinary codegen stays direct. */
|
|
15205
|
-
function preparePresentationHydration(body, node, ctx) {
|
|
15223
|
+
function preparePresentationHydration(body, node, ctx, hostEnd) {
|
|
15206
15224
|
const proof = node._octanePresentationHydration;
|
|
15207
15225
|
if (!proof || ctx.mode === 'server' || ctx._universalRuntimeUnit != null) return body;
|
|
15208
15226
|
const writers = new Set([
|
|
15209
15227
|
'bindSignalAttribute',
|
|
15228
|
+
'bindSignalValue',
|
|
15229
|
+
'queueNativeChangeDiagnostic',
|
|
15210
15230
|
'setEventHandler',
|
|
15211
15231
|
'setAttribute',
|
|
15212
15232
|
'setPlainAttribute',
|
|
@@ -15272,6 +15292,7 @@ function preparePresentationHydration(body, node, ctx) {
|
|
|
15272
15292
|
const visit = (value, rewrite) => {
|
|
15273
15293
|
if (!value || typeof value !== 'object') return value;
|
|
15274
15294
|
if (Array.isArray(value)) return value.map((item) => visit(item, rewrite));
|
|
15295
|
+
if (proof.host && value._octanePresentationUnbound) return value;
|
|
15275
15296
|
if (!value.type || /Function/.test(value.type)) return value;
|
|
15276
15297
|
if (value.type === 'ReturnStatement' && !value._octanePendingOutputGuard) supported = false;
|
|
15277
15298
|
const helper =
|
|
@@ -15343,11 +15364,15 @@ function preparePresentationHydration(body, node, ctx) {
|
|
|
15343
15364
|
)
|
|
15344
15365
|
: result;
|
|
15345
15366
|
};
|
|
15346
|
-
|
|
15367
|
+
const captured = proof.host ? body.slice(0, hostEnd) : body;
|
|
15368
|
+
visit(captured, false);
|
|
15347
15369
|
const frame = b.id(allocCompilerName(ctx, '__presentationHydration'));
|
|
15348
15370
|
const completed = b.id(allocCompilerName(ctx, '__presentationComplete'));
|
|
15349
|
-
const failure =
|
|
15350
|
-
|
|
15371
|
+
const failure =
|
|
15372
|
+
proof.structural || proof.nativeControl || proof.host
|
|
15373
|
+
? b.id(allocCompilerName(ctx, '__presentationFailure'))
|
|
15374
|
+
: null;
|
|
15375
|
+
const statements = supported ? visit(captured, true) : captured;
|
|
15351
15376
|
let directiveEnd = 0;
|
|
15352
15377
|
while (
|
|
15353
15378
|
statements[directiveEnd]?.type === 'ExpressionStatement' &&
|
|
@@ -15359,21 +15384,33 @@ function preparePresentationHydration(body, node, ctx) {
|
|
|
15359
15384
|
b.id('__s'),
|
|
15360
15385
|
b.literal(proof.id),
|
|
15361
15386
|
b.literal(supported),
|
|
15362
|
-
...(proof.structural ? [b.literal(true)] : []),
|
|
15363
|
-
...(proof.conditionalRest ? [b.literal(true)] : []),
|
|
15387
|
+
...(proof.structural || proof.host ? [b.literal(proof.structural === true)] : []),
|
|
15388
|
+
...(proof.conditionalRest || proof.host ? [b.literal(proof.conditionalRest === true)] : []),
|
|
15389
|
+
...(proof.host ? [b.literal(true)] : []),
|
|
15364
15390
|
);
|
|
15365
15391
|
// A new, unsupported writer can still render normally, but must reject an
|
|
15366
15392
|
// attempted lease before publishing anything. It never opens a frame.
|
|
15367
15393
|
if (!supported)
|
|
15368
|
-
return [...
|
|
15394
|
+
return [...body.slice(0, directiveEnd), b.stmt(begin), ...body.slice(directiveEnd)];
|
|
15369
15395
|
return [
|
|
15370
15396
|
...statements.slice(0, directiveEnd),
|
|
15371
|
-
b.const(frame, begin),
|
|
15397
|
+
proof.host ? b.let(frame, begin) : b.const(frame, begin),
|
|
15372
15398
|
b.let(completed, b.literal(false)),
|
|
15373
15399
|
b.try(
|
|
15374
15400
|
b.block([
|
|
15375
15401
|
...statements.slice(directiveEnd),
|
|
15376
15402
|
b.stmt(b.assignment('=', completed, b.literal(true))),
|
|
15403
|
+
...(proof.host
|
|
15404
|
+
? [
|
|
15405
|
+
// Descendants hydrate under their own receipts. The completed
|
|
15406
|
+
// host frame remains pending in this root's commit capture.
|
|
15407
|
+
b.stmt(
|
|
15408
|
+
b.call(requireRuntimeForContext(ctx, 'endPresentationHydration'), frame, completed),
|
|
15409
|
+
),
|
|
15410
|
+
b.stmt(b.assignment('=', frame, b.literal(null))),
|
|
15411
|
+
...body.slice(hostEnd),
|
|
15412
|
+
]
|
|
15413
|
+
: []),
|
|
15377
15414
|
]),
|
|
15378
15415
|
failure === null
|
|
15379
15416
|
? null
|
|
@@ -15957,6 +15994,8 @@ function compileFunctionBody(node, ctx, name, parentNs = 'html', cssHash = null,
|
|
|
15957
15994
|
);
|
|
15958
15995
|
bodyStatements.push(...plan.everyRender);
|
|
15959
15996
|
}
|
|
15997
|
+
// Native host presentation ends before any child slot can execute.
|
|
15998
|
+
let presentationHostEnd = bodyStatements.length;
|
|
15960
15999
|
if (plan?.after) bodyStatements.push(...plan.after);
|
|
15961
16000
|
// Hoisted `<title>`/`<meta>`/`<link>` → headBlock into document.head
|
|
15962
16001
|
// (out-of-band; re-applied each render for reactivity, removed on unmount).
|
|
@@ -15985,6 +16024,7 @@ function compileFunctionBody(node, ctx, name, parentNs = 'html', cssHash = null,
|
|
|
15985
16024
|
bodyStatements.unshift(
|
|
15986
16025
|
inheritOriginLoc(b.const('__block', b.member(b.id('__s'), 'block')), node),
|
|
15987
16026
|
);
|
|
16027
|
+
presentationHostEnd++;
|
|
15988
16028
|
}
|
|
15989
16029
|
// PROPS-FIRST convention: `(…userProps, __s, __extra)`. The scope is the 2nd arg
|
|
15990
16030
|
// (a placeholder leads when there are no user params), so a plain function
|
|
@@ -16001,7 +16041,12 @@ function compileFunctionBody(node, ctx, name, parentNs = 'html', cssHash = null,
|
|
|
16001
16041
|
ctx.currentMapTemps = prevMapTemps;
|
|
16002
16042
|
// ONE FunctionDeclaration node — the caller prints it (once, with the full
|
|
16003
16043
|
// esrap map for top-level components) or embeds it in an enclosing body.
|
|
16004
|
-
const presentationBody = preparePresentationHydration(
|
|
16044
|
+
const presentationBody = preparePresentationHydration(
|
|
16045
|
+
bodyStatements,
|
|
16046
|
+
node,
|
|
16047
|
+
ctx,
|
|
16048
|
+
presentationHostEnd,
|
|
16049
|
+
);
|
|
16005
16050
|
ctx.presentationHydration = previousPresentationHydration;
|
|
16006
16051
|
const emittedFunction = b.function_declaration(
|
|
16007
16052
|
b.id(name, node.id ?? node),
|
|
@@ -24344,6 +24389,17 @@ function planJsx(
|
|
|
24344
24389
|
}
|
|
24345
24390
|
}
|
|
24346
24391
|
for (const b of elementBindings) {
|
|
24392
|
+
if (ctx.presentationHydration?.host && b.path.length === 0) {
|
|
24393
|
+
const name =
|
|
24394
|
+
b.kind === 'ref'
|
|
24395
|
+
? 'ref'
|
|
24396
|
+
: b.kind === 'event' || b.kind === 'event-bundle'
|
|
24397
|
+
? b.slotKeyOrigin?.name
|
|
24398
|
+
: b.name;
|
|
24399
|
+
b.presentationUnbound =
|
|
24400
|
+
typeof name === 'string' &&
|
|
24401
|
+
ctx.presentationHydration.unboundAttributes.has(name.toLowerCase());
|
|
24402
|
+
}
|
|
24347
24403
|
const sharesSpreadHost = spreadPaths.has(b.path.join(','));
|
|
24348
24404
|
const sharesEventSlot =
|
|
24349
24405
|
(b.kind === 'event' || b.kind === 'event-bundle') &&
|
|
@@ -25912,11 +25968,13 @@ function propertyIsEnumerableCall(objNode, name) {
|
|
|
25912
25968
|
);
|
|
25913
25969
|
}
|
|
25914
25970
|
|
|
25915
|
-
//
|
|
25916
|
-
//
|
|
25971
|
+
// Ref teardown reads the live bag because updates replace refs. An interrupted
|
|
25972
|
+
// mount has no committed bag and never attached its refs, so its cleanup is inert.
|
|
25917
25973
|
function cleanupsPush(fnNode) {
|
|
25918
25974
|
const lazyArray = b.assignment('??=', b.member(b.id('__s'), 'cleanups'), b.array([]));
|
|
25919
|
-
return b.stmt(
|
|
25975
|
+
return b.stmt(
|
|
25976
|
+
b.call(b.member(lazyArray, 'push'), b.arrow([], b.logical('&&', b.id('_b'), fnNode.body))),
|
|
25977
|
+
);
|
|
25920
25978
|
}
|
|
25921
25979
|
|
|
25922
25980
|
/**
|
|
@@ -26107,7 +26165,10 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
26107
26165
|
if (knownExpression !== bind.expr) bind = { ...bind, expr: knownExpression };
|
|
26108
26166
|
if (bind.deferred) return emitDeferredMount(bind, elVar, bag);
|
|
26109
26167
|
const org = bindingOrigin(bind);
|
|
26110
|
-
const st = (node) =>
|
|
26168
|
+
const st = (node) => {
|
|
26169
|
+
const emitted = inheritOriginLoc(node, org);
|
|
26170
|
+
return bind.presentationUnbound ? { ...emitted, _octanePresentationUnbound: true } : emitted;
|
|
26171
|
+
};
|
|
26111
26172
|
const el = () => hostVarNode(elVar);
|
|
26112
26173
|
const local = (key) => b.id(bag.local(key));
|
|
26113
26174
|
const hostKey = `_el$${bind.id}`;
|
|
@@ -26315,9 +26376,8 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
26315
26376
|
]),
|
|
26316
26377
|
);
|
|
26317
26378
|
}
|
|
26318
|
-
// The
|
|
26319
|
-
//
|
|
26320
|
-
// `_host$` field is re-written by updates, so the read must be live.
|
|
26379
|
+
// The `_host$` field is re-written by updates, so cleanup reads the live
|
|
26380
|
+
// bag. cleanupsPush skips that read when mount never committed it.
|
|
26321
26381
|
const cleanup = b.arrow(
|
|
26322
26382
|
[],
|
|
26323
26383
|
b.call(
|
|
@@ -26494,9 +26554,8 @@ function emitBindingMount(bind, elVar, bag) {
|
|
|
26494
26554
|
// (queueRefDetach: unmount cleanups run mid-render, and a state-setter
|
|
26495
26555
|
// ref firing null synchronously can render before a replacement
|
|
26496
26556
|
// element's deferred attach — commit-phase detach batches the two).
|
|
26497
|
-
// The
|
|
26498
|
-
//
|
|
26499
|
-
// `_sp$` field is re-written by updates, so the read must be live.
|
|
26557
|
+
// The `_sp$` field is re-written by updates, so cleanup reads the live
|
|
26558
|
+
// bag. cleanupsPush skips that read when mount never committed it.
|
|
26500
26559
|
const flags = bind.skipFormControls
|
|
26501
26560
|
? [b.literal(bind.skipDangerouslySetInnerHTML === true), b.literal(true)]
|
|
26502
26561
|
: bind.skipDangerouslySetInnerHTML
|
|
@@ -26632,7 +26691,10 @@ function emitBindingUpdate(bind, bag, inlineBindingGuards = false) {
|
|
|
26632
26691
|
if (knownExpression !== bind.expr) bind = { ...bind, expr: knownExpression };
|
|
26633
26692
|
if (bind.mountOnly) return null;
|
|
26634
26693
|
const org = bindingOrigin(bind);
|
|
26635
|
-
const st = (node) =>
|
|
26694
|
+
const st = (node) => {
|
|
26695
|
+
const emitted = inheritOriginLoc(node, org);
|
|
26696
|
+
return bind.presentationUnbound ? { ...emitted, _octanePresentationUnbound: true } : emitted;
|
|
26697
|
+
};
|
|
26636
26698
|
const V = () => b.id('_v');
|
|
26637
26699
|
// 1-char bag field names (see makeBag) — resolved from the same registry the
|
|
26638
26700
|
// mount pass registered them in; an unmounted field throws at compile time.
|
|
@@ -55,7 +55,9 @@ export function needsBindingProgram(node, isUnbound) {
|
|
|
55
55
|
if (!/^[a-z]/.test(tagOf(node) ?? '')) return true;
|
|
56
56
|
if (
|
|
57
57
|
(node.openingElement?.attributes ?? node.attributes ?? []).some(
|
|
58
|
-
(attr) =>
|
|
58
|
+
(attr) =>
|
|
59
|
+
(rawName(attr) === 'ref' || /^on[A-Z]/.test(rawName(attr) ?? '')) &&
|
|
60
|
+
!isUnbound(attrValue(attr)),
|
|
59
61
|
)
|
|
60
62
|
)
|
|
61
63
|
return true;
|
|
@@ -590,6 +592,7 @@ export function planBindingProgram(fn, render, context) {
|
|
|
590
592
|
if (context.annotationsOnly) return name === 'class' || name === 'className';
|
|
591
593
|
if (name !== 'ref' && !/^on[A-Z]/.test(name ?? '')) return true;
|
|
592
594
|
const expression = attrValue(attr);
|
|
595
|
+
if (isUnbound(expression)) return true;
|
|
593
596
|
// Event/ref callbacks are native owner adapters, not eager projections.
|
|
594
597
|
assertAdapter(expression);
|
|
595
598
|
if (
|
|
@@ -695,6 +698,19 @@ export function planBindingProgram(fn, render, context) {
|
|
|
695
698
|
selfNs,
|
|
696
699
|
tag === 'textarea' || opaqueChildren ? null : 0,
|
|
697
700
|
];
|
|
701
|
+
if (
|
|
702
|
+
tag === 'textarea' &&
|
|
703
|
+
selfNs === 0 &&
|
|
704
|
+
normalizedChildren.length === 0 &&
|
|
705
|
+
(node.attributes ?? []).some(
|
|
706
|
+
(attr) => rawName(attr) === 'value' && native.unbound.has(unwrap(attrValue(attr))),
|
|
707
|
+
) &&
|
|
708
|
+
!(node.attributes ?? []).some((attr) => rawName(attr) === 'dangerouslySetInnerHTML')
|
|
709
|
+
) {
|
|
710
|
+
// Its SSR text is native value content, not an arbitrary opaque subtree.
|
|
711
|
+
// The handoff validates the live text-only topology before accepting it.
|
|
712
|
+
nativeNode.push('value');
|
|
713
|
+
}
|
|
698
714
|
if (opaqueChildren)
|
|
699
715
|
constructionError ??=
|
|
700
716
|
'This view contains externally owned children that cannot be constructed by a binding program.';
|
|
@@ -710,7 +726,7 @@ export function planBindingProgram(fn, render, context) {
|
|
|
710
726
|
}
|
|
711
727
|
const attributes = createTemplateIr();
|
|
712
728
|
for (const attr of node.attributes ?? []) {
|
|
713
|
-
if (attr._octaneKnownAttributeSpread) continue;
|
|
729
|
+
if (attr._octaneKnownAttributeSpread && !attr._octaneKnownAttributeSpread.unbound) continue;
|
|
714
730
|
if (rawName(attr) === 'ref' || /^on[A-Z]/.test(rawName(attr) ?? '')) continue;
|
|
715
731
|
if (attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') {
|
|
716
732
|
constructionError ??=
|