octane 0.2.13 → 0.2.15
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-claims.cjs +28 -0
- package/dist/cjs/dom-binding-handoff.cjs +2 -2
- package/dist/cjs/dom-bindings.cjs +105 -13
- package/dist/cjs/runtime.cjs +311 -55
- 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 +30 -3
- package/dist/compiler/dom-bindings.js +115 -34
- package/dist/compiler/volar.js +58 -17
- package/dist/constants.d.ts +2 -2
- package/dist/constants.js +2 -2
- package/dist/dom-binding-claims.d.ts +1 -0
- package/dist/dom-binding-claims.js +4 -0
- 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 +5 -2
- package/dist/dom-bindings.js +105 -13
- package/dist/runtime.d.ts +34 -3
- package/dist/runtime.js +314 -55
- 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
|
@@ -248,7 +248,7 @@ function importedProjectionCall(node, imports, lexical, parameterScope) {
|
|
|
248
248
|
const callee = unwrap(node?.callee);
|
|
249
249
|
let importedRoot = callee;
|
|
250
250
|
while (importedRoot?.type === 'MemberExpression' && !importedRoot.computed)
|
|
251
|
-
importedRoot = importedRoot.object;
|
|
251
|
+
importedRoot = unwrap(importedRoot.object);
|
|
252
252
|
const namespace =
|
|
253
253
|
importedRoot !== callee &&
|
|
254
254
|
importedRoot?.type === 'Identifier' &&
|
|
@@ -695,6 +695,8 @@ function planView(fn, filename, source, imports, lexical, native = null) {
|
|
|
695
695
|
const signalIndices = [];
|
|
696
696
|
const styleIndices = [];
|
|
697
697
|
const projectionGroups = [];
|
|
698
|
+
const providerBindings = new Set();
|
|
699
|
+
const unboundAttributes = new Set();
|
|
698
700
|
const classAttributes = new Map();
|
|
699
701
|
const id = `d:${strongHash(`octane:dom-bindings:2\0${filename}\0${fn.id.name}\0${source}`)}`;
|
|
700
702
|
const parameterScope = lexical.nodeScopes.get(fn.body) ?? lexical.rootScope;
|
|
@@ -702,14 +704,15 @@ function planView(fn, filename, source, imports, lexical, native = null) {
|
|
|
702
704
|
let addressed = false;
|
|
703
705
|
const markUnbound = (expression, opaque = false) => {
|
|
704
706
|
const value = unwrap(expression);
|
|
707
|
+
const callee = unwrap(value?.callee);
|
|
705
708
|
const external =
|
|
706
|
-
value?.type === 'CallExpression' &&
|
|
707
|
-
? imports.get(
|
|
709
|
+
value?.type === 'CallExpression' && callee?.type === 'Identifier'
|
|
710
|
+
? imports.get(callee.name)
|
|
708
711
|
: null;
|
|
709
712
|
if (
|
|
710
713
|
external?.source !== 'octane/behavior' ||
|
|
711
714
|
external.imported !== 'unbound' ||
|
|
712
|
-
lexical.resolveBinding(lexical.nodeScopes.get(
|
|
715
|
+
lexical.resolveBinding(lexical.nodeScopes.get(callee), callee.name)?.scope !==
|
|
713
716
|
lexical.rootScope
|
|
714
717
|
)
|
|
715
718
|
return false;
|
|
@@ -898,6 +901,18 @@ function planView(fn, filename, source, imports, lexical, native = null) {
|
|
|
898
901
|
const expression = child.type === 'JSXExpressionContainer' ? child.expression : null;
|
|
899
902
|
return expression === null || !markUnbound(expression, true);
|
|
900
903
|
});
|
|
904
|
+
// A scalar leaf has one stable Text node (or no node for empty SSR).
|
|
905
|
+
// Structural or opaque children retain their existing compiler contract.
|
|
906
|
+
const child = children.length === 1 ? children[0] : null;
|
|
907
|
+
const expression = child?.type === 'JSXExpressionContainer' ? child.expression : null;
|
|
908
|
+
const text =
|
|
909
|
+
tag !== 'textarea' &&
|
|
910
|
+
authoredChildren.length === children.length &&
|
|
911
|
+
(child?.type === 'JSXText' ||
|
|
912
|
+
(expression?.type === 'Literal' && typeof expression.value === 'string') ||
|
|
913
|
+
(expression?.type === 'TSAsExpression' &&
|
|
914
|
+
(expression.typeAnnotation.type === 'TSStringKeyword' ||
|
|
915
|
+
expression.typeAnnotation.type === 'TSNumberKeyword')));
|
|
901
916
|
const opaqueChildren = authoredChildren.length !== children.length;
|
|
902
917
|
const openChildren = tag !== 'textarea' && opaqueChildren && children.length > 0;
|
|
903
918
|
if (tag === 'textarea' && children.some((child) => child.type !== 'JSXText'))
|
|
@@ -910,15 +925,17 @@ function planView(fn, filename, source, imports, lexical, native = null) {
|
|
|
910
925
|
parent,
|
|
911
926
|
tag,
|
|
912
927
|
ns,
|
|
913
|
-
(opaqueChildren && !openChildren) || tag === 'textarea' ? null : children.length,
|
|
914
|
-
...(openChildren ? [true] : []),
|
|
928
|
+
(opaqueChildren && !openChildren) || tag === 'textarea' ? null : text ? 0 : children.length,
|
|
929
|
+
...(text ? [null, 1] : openChildren ? [true] : []),
|
|
915
930
|
]);
|
|
916
931
|
elements.push(element);
|
|
917
932
|
const owned = new Set();
|
|
918
933
|
const externalNames = new Set();
|
|
919
934
|
const knownFields = new Set();
|
|
920
935
|
for (const attr of element.openingElement?.attributes ?? element.attributes ?? []) {
|
|
921
|
-
|
|
936
|
+
// A provider proves normal-renderer fields, not early ownership. External
|
|
937
|
+
// spreads retain the ordinary unbound checks and are stripped below.
|
|
938
|
+
if (attr._octaneKnownAttributeSpread && !attr._octaneKnownAttributeSpread.unbound) {
|
|
922
939
|
const argument = attr.argument ?? attr.value.expression;
|
|
923
940
|
// The compiler verified the exact imported factory against its
|
|
924
941
|
// provider contract. Its arguments still need the ordinary proof.
|
|
@@ -956,6 +973,7 @@ function planView(fn, filename, source, imports, lexical, native = null) {
|
|
|
956
973
|
if (!group) signalIndices.push(bindings.length);
|
|
957
974
|
bindings.push([index, bindingKind(tag, name), name]);
|
|
958
975
|
}
|
|
976
|
+
providerBindings.add(bindings.length - 1);
|
|
959
977
|
values.push(
|
|
960
978
|
group
|
|
961
979
|
? inheritHookMemoOrigin(
|
|
@@ -979,26 +997,31 @@ function planView(fn, filename, source, imports, lexical, native = null) {
|
|
|
979
997
|
error(filename, attr, 'binding attribute spreads must be explicitly unbound');
|
|
980
998
|
if (bindings.some((binding) => binding[0] === index))
|
|
981
999
|
error(filename, attr, 'unbound attribute spreads must precede owned binding attributes');
|
|
1000
|
+
const addExternalName = (raw, node) => {
|
|
1001
|
+
const name = (
|
|
1002
|
+
raw === 'className' ? 'class' : (ATTRIBUTE_ALIASES.get(raw) ?? raw)
|
|
1003
|
+
).toLowerCase();
|
|
1004
|
+
if (
|
|
1005
|
+
FORBIDDEN_ATTRS.has(name) ||
|
|
1006
|
+
name.startsWith('on') ||
|
|
1007
|
+
name.startsWith('data-octane-class-')
|
|
1008
|
+
)
|
|
1009
|
+
error(
|
|
1010
|
+
filename,
|
|
1011
|
+
node,
|
|
1012
|
+
`unbound spreads cannot supply reserved or structural attribute ${JSON.stringify(raw)}`,
|
|
1013
|
+
);
|
|
1014
|
+
externalNames.add(name);
|
|
1015
|
+
};
|
|
982
1016
|
const external = unwrap(unwrap(attr.argument).arguments[0]);
|
|
983
|
-
if (
|
|
1017
|
+
if (attr._octaneKnownAttributeSpread) {
|
|
1018
|
+
for (const raw of attr._octaneKnownAttributeSpread.fields) addExternalName(raw, attr);
|
|
1019
|
+
} else if (external?.type === 'ObjectExpression') {
|
|
984
1020
|
for (const property of external.properties) {
|
|
985
1021
|
if (property.type !== 'Property' || property.computed) continue;
|
|
986
1022
|
const raw = property.key.name ?? property.key.value;
|
|
987
1023
|
if (typeof raw !== 'string') continue;
|
|
988
|
-
|
|
989
|
-
raw === 'className' ? 'class' : (ATTRIBUTE_ALIASES.get(raw) ?? raw)
|
|
990
|
-
).toLowerCase();
|
|
991
|
-
if (
|
|
992
|
-
FORBIDDEN_ATTRS.has(name) ||
|
|
993
|
-
name.startsWith('on') ||
|
|
994
|
-
name.startsWith('data-octane-class-')
|
|
995
|
-
)
|
|
996
|
-
error(
|
|
997
|
-
filename,
|
|
998
|
-
property,
|
|
999
|
-
`unbound spreads cannot supply reserved or structural attribute ${JSON.stringify(raw)}`,
|
|
1000
|
-
);
|
|
1001
|
-
externalNames.add(name);
|
|
1024
|
+
addExternalName(raw, property);
|
|
1002
1025
|
}
|
|
1003
1026
|
}
|
|
1004
1027
|
continue;
|
|
@@ -1022,6 +1045,16 @@ function planView(fn, filename, source, imports, lexical, native = null) {
|
|
|
1022
1045
|
nodes[index][3] = null;
|
|
1023
1046
|
continue;
|
|
1024
1047
|
}
|
|
1048
|
+
const value =
|
|
1049
|
+
attr.value === null
|
|
1050
|
+
? b.literal(true)
|
|
1051
|
+
: unwrap(
|
|
1052
|
+
attr.value?.type === 'JSXExpressionContainer' ? attr.value.expression : attr.value,
|
|
1053
|
+
);
|
|
1054
|
+
if ((lower === 'ref' || /^on[A-Z]/.test(raw)) && markUnbound(value)) {
|
|
1055
|
+
unboundAttributes.add(lower);
|
|
1056
|
+
continue;
|
|
1057
|
+
}
|
|
1025
1058
|
if (
|
|
1026
1059
|
!/^[A-Za-z_][A-Za-z0-9_.:-]*$/.test(name) ||
|
|
1027
1060
|
lower.startsWith('on') ||
|
|
@@ -1030,13 +1063,10 @@ function planView(fn, filename, source, imports, lexical, native = null) {
|
|
|
1030
1063
|
) {
|
|
1031
1064
|
error(filename, attr, `attribute ${JSON.stringify(raw)} is not supported in binding views`);
|
|
1032
1065
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
attr.value?.type === 'JSXExpressionContainer' ? attr.value.expression : attr.value,
|
|
1038
|
-
);
|
|
1039
|
-
if (markUnbound(value)) continue;
|
|
1066
|
+
if (markUnbound(value)) {
|
|
1067
|
+
unboundAttributes.add(lower);
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1040
1070
|
if (value?.type !== 'Literal' && externalNames.has(lower))
|
|
1041
1071
|
error(
|
|
1042
1072
|
filename,
|
|
@@ -1139,11 +1169,35 @@ function planView(fn, filename, source, imports, lexical, native = null) {
|
|
|
1139
1169
|
add(index, kind, name, value, attr);
|
|
1140
1170
|
}
|
|
1141
1171
|
}
|
|
1142
|
-
if (
|
|
1172
|
+
if (text) {
|
|
1173
|
+
if (expression) add(index, 'text', '#text', expression, child);
|
|
1174
|
+
} else if (tag !== 'textarea') {
|
|
1143
1175
|
for (const child of children) visit(child, index, ns, [...ancestors, tag]);
|
|
1144
1176
|
}
|
|
1145
1177
|
};
|
|
1146
1178
|
visit(render, -1, native?.namespace ?? 0, native?.ancestors ?? []);
|
|
1179
|
+
// Opaque descendants are outside this receipt. Only a scalar native host's
|
|
1180
|
+
// declared presentation channels can transfer independently of those children.
|
|
1181
|
+
const hostHandoff =
|
|
1182
|
+
nodes.length === 1 &&
|
|
1183
|
+
nodes[0][3] === null &&
|
|
1184
|
+
!addressed &&
|
|
1185
|
+
!['textarea', 'input', 'select'].includes(nodes[0][1]) &&
|
|
1186
|
+
bindings.length > 0 &&
|
|
1187
|
+
bindings.every(
|
|
1188
|
+
(binding, index) =>
|
|
1189
|
+
binding[0] === 0 &&
|
|
1190
|
+
(['class', 'styleProperty', 'styleAttribute', 'styleObject'].includes(binding[1]) ||
|
|
1191
|
+
providerBindings.has(index)),
|
|
1192
|
+
) &&
|
|
1193
|
+
[...(render.openingElement?.attributes ?? render.attributes ?? [])].every(
|
|
1194
|
+
(attr) =>
|
|
1195
|
+
!['JSXSpreadAttribute', 'SpreadAttribute'].includes(attr.type) ||
|
|
1196
|
+
(attr._octaneKnownAttributeSpread && !attr._octaneKnownAttributeSpread.unbound),
|
|
1197
|
+
) &&
|
|
1198
|
+
!(render.openingElement?.attributes ?? render.attributes ?? []).some(
|
|
1199
|
+
(attr) => attrName(attr)?.toLowerCase() === 'dangerouslysetinnerhtml',
|
|
1200
|
+
);
|
|
1147
1201
|
return {
|
|
1148
1202
|
fn,
|
|
1149
1203
|
render,
|
|
@@ -1159,6 +1213,8 @@ function planView(fn, filename, source, imports, lexical, native = null) {
|
|
|
1159
1213
|
unbound,
|
|
1160
1214
|
classAttributes,
|
|
1161
1215
|
id,
|
|
1216
|
+
hostHandoff,
|
|
1217
|
+
unboundAttributes,
|
|
1162
1218
|
};
|
|
1163
1219
|
}
|
|
1164
1220
|
|
|
@@ -1181,6 +1237,7 @@ function scalarProperties(
|
|
|
1181
1237
|
b.prop('init', b.id('nodes'), literalData(plan.nodes)),
|
|
1182
1238
|
b.prop('init', b.id('bindings'), literalData(plan.bindings)),
|
|
1183
1239
|
b.prop('init', b.id('project'), project),
|
|
1240
|
+
...(plan.hostHandoff ? [b.prop('init', b.id('handoff'), b.literal('host'))] : []),
|
|
1184
1241
|
...(plan.signalIndices.length
|
|
1185
1242
|
? [
|
|
1186
1243
|
b.prop('init', b.id('signalIndices'), literalData(plan.signalIndices)),
|
|
@@ -1221,6 +1278,8 @@ function projectionDependencies(plan, lexical) {
|
|
|
1221
1278
|
}
|
|
1222
1279
|
});
|
|
1223
1280
|
collect(plan.expressions ?? plan.values);
|
|
1281
|
+
collect(plan.scalar?.values);
|
|
1282
|
+
collect(plan.scalar?.projections?.map((declaration) => declaration.declarations[0].init));
|
|
1224
1283
|
collect(plan.projections?.map((declaration) => declaration.declarations[0].init));
|
|
1225
1284
|
return needed;
|
|
1226
1285
|
}
|
|
@@ -1435,7 +1494,15 @@ function projectProgram(ast, plan, filename, lexical) {
|
|
|
1435
1494
|
b.object(
|
|
1436
1495
|
scalarProperties(
|
|
1437
1496
|
plan.scalar,
|
|
1438
|
-
|
|
1497
|
+
inheritHookMemoOrigin(
|
|
1498
|
+
b.arrow(
|
|
1499
|
+
plan.fn.params,
|
|
1500
|
+
plan.scalar.projections.length > 0
|
|
1501
|
+
? b.block([...plan.scalar.projections, b.return(b.array(plan.scalar.values))])
|
|
1502
|
+
: b.array(plan.scalar.values),
|
|
1503
|
+
),
|
|
1504
|
+
plan.fn,
|
|
1505
|
+
),
|
|
1439
1506
|
plan.scalar.bindings.some((binding) => binding[1] === 'classGroup')
|
|
1440
1507
|
? b.member(root, 'createClassGroup')
|
|
1441
1508
|
: null,
|
|
@@ -1876,12 +1943,13 @@ export function prepareDomBindings(ast, source, filename, selectedExport, helper
|
|
|
1876
1943
|
: new Map();
|
|
1877
1944
|
const isUnbound = (expression) => {
|
|
1878
1945
|
const value = unwrap(expression);
|
|
1879
|
-
|
|
1880
|
-
|
|
1946
|
+
const callee = unwrap(value?.callee);
|
|
1947
|
+
if (value?.type !== 'CallExpression' || callee?.type !== 'Identifier') return false;
|
|
1948
|
+
const imported = imports.get(callee.name);
|
|
1881
1949
|
return (
|
|
1882
1950
|
imported?.source === 'octane/behavior' &&
|
|
1883
1951
|
imported.imported === 'unbound' &&
|
|
1884
|
-
lexical.resolveBinding(lexical.nodeScopes.get(
|
|
1952
|
+
lexical.resolveBinding(lexical.nodeScopes.get(callee), callee.name)?.scope ===
|
|
1885
1953
|
lexical.rootScope
|
|
1886
1954
|
);
|
|
1887
1955
|
};
|
|
@@ -2132,6 +2200,9 @@ export function prepareDomBindings(ast, source, filename, selectedExport, helper
|
|
|
2132
2200
|
_octanePresentationHydration: {
|
|
2133
2201
|
id: plan.id,
|
|
2134
2202
|
supported: fixedPresentation || structuralPresentation,
|
|
2203
|
+
...(field('nodes').elements.some((node) => node.elements[5]?.value === 'value')
|
|
2204
|
+
? { nativeControl: true }
|
|
2205
|
+
: {}),
|
|
2135
2206
|
...(structuralPresentation || conditionalRest ? { structural: true } : {}),
|
|
2136
2207
|
...(conditionalRest ? { conditionalRest: true } : {}),
|
|
2137
2208
|
},
|
|
@@ -2207,6 +2278,16 @@ export function prepareDomBindings(ast, source, filename, selectedExport, helper
|
|
|
2207
2278
|
);
|
|
2208
2279
|
}
|
|
2209
2280
|
for (const [node, replacement] of rewritten) replacements.set(node, replacement);
|
|
2281
|
+
if (plan.hostHandoff)
|
|
2282
|
+
replacements.set(node, {
|
|
2283
|
+
...mapCow(node, rewritten),
|
|
2284
|
+
_octanePresentationHydration: {
|
|
2285
|
+
id: plan.id,
|
|
2286
|
+
supported: true,
|
|
2287
|
+
host: true,
|
|
2288
|
+
unboundAttributes: plan.unboundAttributes,
|
|
2289
|
+
},
|
|
2290
|
+
});
|
|
2210
2291
|
});
|
|
2211
2292
|
if (selectedExport !== null) {
|
|
2212
2293
|
const plan = plans.get(selectedExport);
|
package/dist/compiler/volar.js
CHANGED
|
@@ -31829,11 +31829,10 @@ var OCTANE_PLATFORM = {
|
|
|
31829
31829
|
}
|
|
31830
31830
|
};
|
|
31831
31831
|
var octaneTransform = createJsxTransform(OCTANE_PLATFORM);
|
|
31832
|
-
function
|
|
31832
|
+
function projectNativeDomReads(ast, contracts) {
|
|
31833
31833
|
const attributes = new Set(
|
|
31834
31834
|
contracts?.flatMap((contract) => contract.jsxAttribute ? [contract.jsxAttribute] : [])
|
|
31835
31835
|
);
|
|
31836
|
-
if (attributes.size === 0) return ast;
|
|
31837
31836
|
const names = /* @__PURE__ */ new Set();
|
|
31838
31837
|
const walk2 = (node, replace) => {
|
|
31839
31838
|
if (!node || typeof node !== "object") return node;
|
|
@@ -31858,20 +31857,54 @@ function projectNativeAttributeReads(ast, contracts) {
|
|
|
31858
31857
|
let helper = "__octane_nativeAttributeValue";
|
|
31859
31858
|
while (names.has(helper)) helper += "_";
|
|
31860
31859
|
let used = false;
|
|
31860
|
+
const read = (value) => {
|
|
31861
|
+
used = true;
|
|
31862
|
+
return inheritHookMemoOrigin(builders_exports.call(builders_exports.id(helper), value), value);
|
|
31863
|
+
};
|
|
31864
|
+
const projectText = (node) => {
|
|
31865
|
+
if (!node) return node;
|
|
31866
|
+
if (["TSAsExpression", "TSTypeAssertion", "TSSatisfiesExpression"].includes(node.type) && node.typeAnnotation?.type === "TSStringKeyword") {
|
|
31867
|
+
used = true;
|
|
31868
|
+
const expression = projectText(node.expression);
|
|
31869
|
+
return {
|
|
31870
|
+
...node,
|
|
31871
|
+
expression: expression === node.expression ? builders_exports.call(builders_exports.id(helper), expression) : expression
|
|
31872
|
+
};
|
|
31873
|
+
}
|
|
31874
|
+
if ([
|
|
31875
|
+
"TSAsExpression",
|
|
31876
|
+
"TSTypeAssertion",
|
|
31877
|
+
"TSNonNullExpression",
|
|
31878
|
+
"TSSatisfiesExpression",
|
|
31879
|
+
"TSInstantiationExpression",
|
|
31880
|
+
"ParenthesizedExpression"
|
|
31881
|
+
].includes(node.type)) {
|
|
31882
|
+
const expression = projectText(node.expression);
|
|
31883
|
+
return expression === node.expression ? node : { ...node, expression };
|
|
31884
|
+
}
|
|
31885
|
+
return node;
|
|
31886
|
+
};
|
|
31861
31887
|
const projected = walk2(ast, (node) => {
|
|
31862
|
-
if (
|
|
31888
|
+
if (["JSXElement", "JSXFragment", "Element", "Fragment"].includes(node.type)) {
|
|
31889
|
+
node = {
|
|
31890
|
+
...node,
|
|
31891
|
+
children: node.children.map((child) => {
|
|
31892
|
+
if (child.type !== "JSXExpressionContainer") return child;
|
|
31893
|
+
const expression = projectText(child.expression);
|
|
31894
|
+
return expression === child.expression ? child : { ...child, expression };
|
|
31895
|
+
})
|
|
31896
|
+
};
|
|
31897
|
+
}
|
|
31898
|
+
if (node.type !== "JSXOpeningElement" && node.type !== "Element") return node;
|
|
31863
31899
|
const name = node.type === "Element" ? node.id : node.name;
|
|
31864
31900
|
if (name?.type !== "JSXIdentifier" && name?.type !== "Identifier" || typeof name.name !== "string" || name.name[0] !== name.name[0].toLowerCase())
|
|
31865
|
-
return
|
|
31901
|
+
return node;
|
|
31866
31902
|
return {
|
|
31867
31903
|
...node,
|
|
31868
31904
|
attributes: node.attributes.map((attribute) => {
|
|
31869
31905
|
if (!attributes.has(attribute.name?.name ?? attribute.name) || attribute.value?.type !== "JSXExpressionContainer")
|
|
31870
31906
|
return attribute;
|
|
31871
|
-
const result = lowerNativeAttributeReads(attribute.value.expression,
|
|
31872
|
-
used = true;
|
|
31873
|
-
return inheritHookMemoOrigin(builders_exports.call(builders_exports.id(helper), value), value);
|
|
31874
|
-
});
|
|
31907
|
+
const result = lowerNativeAttributeReads(attribute.value.expression, read);
|
|
31875
31908
|
return { ...attribute, value: { ...attribute.value, expression: result.expression } };
|
|
31876
31909
|
})
|
|
31877
31910
|
};
|
|
@@ -32099,7 +32132,7 @@ function compileToVolarMappings(source, filename, options) {
|
|
|
32099
32132
|
const transform = selectOctaneTransform(ast);
|
|
32100
32133
|
const transformed = transform(
|
|
32101
32134
|
projectServerContextCalls(
|
|
32102
|
-
renderer.target === "dom" ?
|
|
32135
|
+
renderer.target === "dom" ? projectNativeDomReads(transformAst, options?.knownAttributeSpreads) : transformAst,
|
|
32103
32136
|
filename
|
|
32104
32137
|
),
|
|
32105
32138
|
source,
|
|
@@ -32251,14 +32284,22 @@ function compileTypesInspection(source, filename, options) {
|
|
|
32251
32284
|
const renderer = resolveRendererForFile(rendererConfig, filename ?? "untitled.tsrx");
|
|
32252
32285
|
const rendererPragma = authoredLeadingPragma(ast, comments) ? null : createRendererTypePragma(renderer, ast);
|
|
32253
32286
|
const transform = selectOctaneTransform(ast);
|
|
32254
|
-
const transformed = transform(
|
|
32255
|
-
|
|
32256
|
-
|
|
32257
|
-
|
|
32258
|
-
|
|
32259
|
-
|
|
32260
|
-
|
|
32261
|
-
|
|
32287
|
+
const transformed = transform(
|
|
32288
|
+
projectServerContextCalls(
|
|
32289
|
+
renderer.target === "dom" ? projectNativeDomReads(ast) : ast,
|
|
32290
|
+
filename
|
|
32291
|
+
),
|
|
32292
|
+
source,
|
|
32293
|
+
filename,
|
|
32294
|
+
{
|
|
32295
|
+
collect: true,
|
|
32296
|
+
loose: true,
|
|
32297
|
+
typeOnly: true,
|
|
32298
|
+
inspect: true,
|
|
32299
|
+
errors,
|
|
32300
|
+
comments: rendererPragma === null ? comments : [rendererPragma, ...comments]
|
|
32301
|
+
}
|
|
32302
|
+
);
|
|
32262
32303
|
markNativeTemplateBodies(ast);
|
|
32263
32304
|
return {
|
|
32264
32305
|
code: transformed.code,
|
package/dist/constants.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export declare const HYDRATION_TEXT_SEP = " ";
|
|
|
50
50
|
* render order) so a hydrating boundary returns synchronously instead of
|
|
51
51
|
* re-suspending. Shared so server emit and client read stay byte-identical.
|
|
52
52
|
*/
|
|
53
|
-
export
|
|
53
|
+
export { SUSPENSE_SCRIPT_ATTR } from './stream-protocol.js';
|
|
54
54
|
export declare const SUSPENSE_RESOLVED_COMMENT = "oct-suspense:";
|
|
55
55
|
export declare const SUSPENSE_RESOLVED_SEED_ATTR = "data-octane-suspense-seeds";
|
|
56
56
|
export declare const SUSPENSE_RESOLVED_NATIVE_ATTR = "data-octane-suspense-signals";
|
|
@@ -105,7 +105,7 @@ export declare const STREAM_SEGMENT_ATTR = "data-oct-s";
|
|
|
105
105
|
/** Per-boundary seed-JSON script attribute (inside the segment). */
|
|
106
106
|
export declare const STREAM_SEED_ATTR = "data-oct-seed";
|
|
107
107
|
/** Renderer-owned executable/data scripts emitted by the streaming protocol. */
|
|
108
|
-
export
|
|
108
|
+
export { STREAM_SCRIPT_ATTR } from './stream-protocol.js';
|
|
109
109
|
/** Hidden carrier for Float sheet resources discovered after the shell; the
|
|
110
110
|
* inline `$OCTRH` call hoists its tags into document.head. */
|
|
111
111
|
export declare const STREAM_RESOURCE_ATTR = "data-oct-fr";
|
package/dist/constants.js
CHANGED
|
@@ -8,7 +8,7 @@ const FOR_BLOCK_OPEN_EMPTY = `<!--${HYDRATION_FOR_EMPTY}-->`;
|
|
|
8
8
|
const FOR_BLOCK_OPEN_ITEMS = `<!--${HYDRATION_FOR_ITEMS}-->`;
|
|
9
9
|
const EMPTY_COMMENT = "<!---->";
|
|
10
10
|
const HYDRATION_TEXT_SEP = " ";
|
|
11
|
-
|
|
11
|
+
import { SUSPENSE_SCRIPT_ATTR } from "./stream-protocol.js";
|
|
12
12
|
const SUSPENSE_RESOLVED_COMMENT = "oct-suspense:";
|
|
13
13
|
const SUSPENSE_RESOLVED_SEED_ATTR = "data-octane-suspense-seeds";
|
|
14
14
|
const SUSPENSE_RESOLVED_NATIVE_ATTR = "data-octane-suspense-signals";
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
import { STREAM_BOUNDARY_ATTR } from "./stream-protocol.js";
|
|
38
38
|
const STREAM_SEGMENT_ATTR = "data-oct-s";
|
|
39
39
|
const STREAM_SEED_ATTR = "data-oct-seed";
|
|
40
|
-
|
|
40
|
+
import { STREAM_SCRIPT_ATTR } from "./stream-protocol.js";
|
|
41
41
|
const STREAM_RESOURCE_ATTR = "data-oct-fr";
|
|
42
42
|
const STREAM_SEED_COMMENT = "oct-seed:";
|
|
43
43
|
import {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const domBindingClaims: WeakMap<Element, Map<string, string | null | undefined>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
1
|
+
import { BINDING_HANDOFF } from './signals/control-handoff.js';
|
|
2
|
+
export { BINDING_HANDOFF };
|
|
3
3
|
/** Current early-owned range, not a license to render a different shape. */
|
|
4
4
|
export interface BindingHandoffRange {
|
|
5
5
|
readonly kind: 'if' | 'view' | 'slot' | 'text';
|
|
@@ -24,13 +24,15 @@ export interface BindingHandoff {
|
|
|
24
24
|
readonly root: Node;
|
|
25
25
|
readonly anchor: Node;
|
|
26
26
|
readonly end?: Node;
|
|
27
|
-
/**
|
|
27
|
+
/** A scalar host's declared native channels; descendants belong to other owners. */
|
|
28
|
+
readonly host?: ReadonlySet<string>;
|
|
29
|
+
/** Revisioned presentations return -1 while publication is in progress. */
|
|
28
30
|
revision?(): number;
|
|
29
31
|
ranges?(): ReadonlyMap<Node, BindingHandoffRange>;
|
|
30
32
|
view?(root: Node): BindingHandoffView | undefined;
|
|
31
33
|
rest?(element: Element, site: number): BindingHandoffRest | undefined;
|
|
32
34
|
valid?(): boolean;
|
|
33
|
-
/** One deferred retry;
|
|
35
|
+
/** One deferred retry; listener storage is allocated only when requested. */
|
|
34
36
|
afterPublication?(callback: () => void): () => void;
|
|
35
37
|
active(): boolean;
|
|
36
38
|
retire(publish?: () => void): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { BINDING_HANDOFF } from "./signals/control-handoff.js";
|
|
2
2
|
const events = /* @__PURE__ */ new WeakMap();
|
|
3
3
|
const delivered = /* @__PURE__ */ new WeakMap();
|
|
4
4
|
function registerBindingEvent(node, type) {
|
|
@@ -15,7 +15,8 @@ export type BindingProgramNode = readonly [
|
|
|
15
15
|
kind: 'element',
|
|
16
16
|
tag: string,
|
|
17
17
|
namespace: 0 | 1,
|
|
18
|
-
children: number | null
|
|
18
|
+
children: number | null,
|
|
19
|
+
nativeControlContent?: 'value'
|
|
19
20
|
] | readonly [parent: number, kind: 'text', text: string] | readonly [parent: number, kind: 'region', site: string];
|
|
20
21
|
export interface BindingRange {
|
|
21
22
|
readonly start: Comment;
|
|
@@ -1250,15 +1250,30 @@ function bindProgram(root, descriptor, source, options, mount, list = descriptor
|
|
|
1250
1250
|
dispose,
|
|
1251
1251
|
() => !busy
|
|
1252
1252
|
);
|
|
1253
|
-
if (mount || descriptor.root.handoff !== true || !instance || descriptor.root.regions.length !== 0 || descriptor.root.bindings.some((binding) => binding[1] === "control") || descriptor.root.nodes.some(
|
|
1253
|
+
if (mount || descriptor.root.handoff !== true || !instance || descriptor.root.regions.length !== 0 || descriptor.root.bindings.some((binding) => binding[1] === "control") || descriptor.root.nodes.some(
|
|
1254
|
+
(node, index) => node[1] === "element" && node[4] === null && !(node[2] === "textarea" && node[3] === 0 && node[5] === "value" && Array.from(instance.nodes[index].childNodes).every((child) => child.nodeType === 3))
|
|
1255
|
+
))
|
|
1254
1256
|
throw new Error(
|
|
1255
1257
|
"Hydration binding leases require an adopted fixed native view without structural regions or controls."
|
|
1256
1258
|
);
|
|
1259
|
+
const topology = handoff === void 0 && descriptor.root.nodes.some((node) => node[1] === "element" && node[5] === "value") ? [instance.range.start, ...instance.nodes, instance.range.end].map(
|
|
1260
|
+
(node) => [node, node.parentNode, node.previousSibling, node.nextSibling]
|
|
1261
|
+
) : void 0;
|
|
1257
1262
|
return handoff ??= {
|
|
1258
1263
|
id: descriptor.id,
|
|
1259
1264
|
root: instance.range.start,
|
|
1260
1265
|
anchor: instance.nodes.find((node) => node.nodeType === 1) ?? instance.range.start,
|
|
1261
1266
|
active: () => !transaction.disposed,
|
|
1267
|
+
...topology === void 0 ? {} : {
|
|
1268
|
+
end: instance.range.end,
|
|
1269
|
+
valid: () => topology.every(
|
|
1270
|
+
([node, parent, before, after]) => node.parentNode === parent && (node === instance.range.start || node.previousSibling === before) && (node === instance.range.end || node.nextSibling === after)
|
|
1271
|
+
) && descriptor.root.nodes.every(
|
|
1272
|
+
(proof, index) => proof[1] !== "element" || (proof[4] === null ? Array.from(instance.nodes[index].childNodes).every(
|
|
1273
|
+
(child) => child.nodeType === 3
|
|
1274
|
+
) : instance.nodes[index].childNodes.length === proof[4])
|
|
1275
|
+
)
|
|
1276
|
+
},
|
|
1262
1277
|
retire: (publish) => {
|
|
1263
1278
|
transaction.preservePresentation = true;
|
|
1264
1279
|
dispose(void 0, publish);
|
package/dist/dom-bindings.d.ts
CHANGED
|
@@ -45,12 +45,13 @@ export type BindingNode = readonly [
|
|
|
45
45
|
tag: string,
|
|
46
46
|
namespace: 0 | 1,
|
|
47
47
|
children: number | null,
|
|
48
|
-
openChildren?: true
|
|
48
|
+
openChildren?: true | null,
|
|
49
|
+
text?: 1
|
|
49
50
|
];
|
|
50
51
|
/** @internal Names and numeric CSS units are resolved by the compiler. */
|
|
51
52
|
export type BindingOperation = readonly [
|
|
52
53
|
node: number,
|
|
53
|
-
kind: 'attr' | 'boolean' | 'aria' | 'class' | 'classToken' | 'classGroup' | 'styleProperty' | 'styleAttribute' | 'styleObject' | 'control' | 'url',
|
|
54
|
+
kind: 'attr' | 'boolean' | 'aria' | 'class' | 'classToken' | 'classGroup' | 'styleProperty' | 'styleAttribute' | 'styleObject' | 'control' | 'url' | 'text',
|
|
54
55
|
name: string,
|
|
55
56
|
unitlessOrGroup?: boolean | number
|
|
56
57
|
];
|
|
@@ -59,6 +60,8 @@ export type BindingValue = string | null | BindingStyleSnapshot;
|
|
|
59
60
|
/** @internal No application function is invoked to discover the DOM topology. */
|
|
60
61
|
export interface CompiledBindings<Props> {
|
|
61
62
|
readonly id: string;
|
|
63
|
+
/** Compiler-proven single native host, with no descendant ownership. */
|
|
64
|
+
readonly handoff?: 'host';
|
|
62
65
|
/** Targets carry compiler-issued addresses when unbound siblings may change. */
|
|
63
66
|
readonly addressed?: true;
|
|
64
67
|
readonly nodes: readonly BindingNode[];
|