@farm.js/react 0.1.0-beta.1 → 0.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -20
- package/dist/compiler-runtime.cjs +411 -1
- package/dist/compiler-runtime.cjs.map +1 -1
- package/dist/compiler-runtime.d.mts +36 -1
- package/dist/compiler-runtime.d.ts +36 -1
- package/dist/compiler-runtime.mjs +411 -1
- package/dist/compiler-runtime.mjs.map +1 -1
- package/dist/vite.cjs +489 -20
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.mjs +489 -20
- package/dist/vite.mjs.map +1 -1
- package/package.json +3 -3
package/dist/vite.mjs
CHANGED
|
@@ -258,6 +258,31 @@ function isTextExpression(expression, statesByValue, safeGlobals) {
|
|
|
258
258
|
function jsxAttributeName(attribute) {
|
|
259
259
|
return types.isJSXIdentifier(attribute.name) ? attribute.name.name : void 0;
|
|
260
260
|
}
|
|
261
|
+
function jsxAttribute(element, name) {
|
|
262
|
+
return element.openingElement.attributes.find(
|
|
263
|
+
(attribute) => types.isJSXAttribute(attribute) && jsxAttributeName(attribute) === name
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
function staticJsxAttributeString(attribute) {
|
|
267
|
+
if (!attribute?.value) return attribute ? "" : void 0;
|
|
268
|
+
if (types.isStringLiteral(attribute.value)) return attribute.value.value;
|
|
269
|
+
if (types.isJSXExpressionContainer(attribute.value) && types.isStringLiteral(attribute.value.expression)) {
|
|
270
|
+
return attribute.value.expression.value;
|
|
271
|
+
}
|
|
272
|
+
return void 0;
|
|
273
|
+
}
|
|
274
|
+
function controlledSelectHasDynamicOptions(element) {
|
|
275
|
+
let dynamic = false;
|
|
276
|
+
types.traverseFast(element, (node) => {
|
|
277
|
+
if (dynamic || !types.isJSXElement(node)) return;
|
|
278
|
+
const tag = node.openingElement.name;
|
|
279
|
+
if (!types.isJSXIdentifier(tag) || tag.name !== "option" && tag.name !== "optgroup") return;
|
|
280
|
+
dynamic = node.openingElement.attributes.some(
|
|
281
|
+
(attribute) => types.isJSXSpreadAttribute(attribute) || types.isJSXAttribute(attribute) && types.isJSXExpressionContainer(attribute.value) && !types.isJSXEmptyExpression(attribute.value.expression)
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
return dynamic;
|
|
285
|
+
}
|
|
261
286
|
function isEmptyConditionalBranch(expression) {
|
|
262
287
|
return types.isNullLiteral(expression) || types.isBooleanLiteral(expression, { value: false });
|
|
263
288
|
}
|
|
@@ -483,7 +508,87 @@ function validateKeyedRowEventHandler(handler) {
|
|
|
483
508
|
});
|
|
484
509
|
return reason;
|
|
485
510
|
}
|
|
486
|
-
function
|
|
511
|
+
function stabilizeDeferredEventCurrentTarget(handler, compilerSetters) {
|
|
512
|
+
const cloned = types.cloneNode(handler, true);
|
|
513
|
+
const eventParameter = cloned.params[0];
|
|
514
|
+
if (!types.isIdentifier(eventParameter) || compilerSetters.size === 0) return cloned;
|
|
515
|
+
const file = expressionFile(cloned);
|
|
516
|
+
const expression = file.program.body[0].expression;
|
|
517
|
+
if (!types.isArrowFunctionExpression(expression) && !types.isFunctionExpression(expression)) {
|
|
518
|
+
return cloned;
|
|
519
|
+
}
|
|
520
|
+
const rootBindingIdentifier = expression.params[0];
|
|
521
|
+
const snapshots = [];
|
|
522
|
+
const snapshotsByProperty = /* @__PURE__ */ new Map();
|
|
523
|
+
const replaceCurrentTarget = (path) => {
|
|
524
|
+
const property = path.node.property;
|
|
525
|
+
const isCurrentTarget = path.node.computed ? types.isStringLiteral(property, { value: "currentTarget" }) : types.isIdentifier(property, { name: "currentTarget" });
|
|
526
|
+
if (!isCurrentTarget || !types.isIdentifier(path.node.object, { name: eventParameter.name }))
|
|
527
|
+
return;
|
|
528
|
+
const binding = path.scope.getBinding(eventParameter.name);
|
|
529
|
+
if (!binding || binding.identifier !== rootBindingIdentifier) return;
|
|
530
|
+
const setterCall = path.findParent(
|
|
531
|
+
(parent2) => parent2.isCallExpression() && types.isIdentifier(parent2.node.callee) && compilerSetters.has(parent2.node.callee.name)
|
|
532
|
+
);
|
|
533
|
+
if (!setterCall) return;
|
|
534
|
+
const deferredFunction = path.findParent(
|
|
535
|
+
(parent2) => parent2.isFunction() && parent2.node !== expression
|
|
536
|
+
);
|
|
537
|
+
if (!deferredFunction || !deferredFunction.findParent((parent2) => parent2 === setterCall))
|
|
538
|
+
return;
|
|
539
|
+
const parent = path.parentPath;
|
|
540
|
+
const propertyRead = (parent?.isMemberExpression() || parent?.isOptionalMemberExpression()) && parent.node.object === path.node ? parent : path;
|
|
541
|
+
const snapshotProperty = propertyRead === path ? "element" : propertyRead.node.computed ? types.isStringLiteral(propertyRead.node.property) ? propertyRead.node.property.value : `computed${snapshots.length}` : types.isIdentifier(propertyRead.node.property) ? propertyRead.node.property.name : `property${snapshots.length}`;
|
|
542
|
+
let snapshot = snapshotsByProperty.get(snapshotProperty);
|
|
543
|
+
if (!snapshot) {
|
|
544
|
+
const suffix = snapshotProperty.replace(/[^A-Za-z0-9_$]/g, " ").replace(
|
|
545
|
+
/(?:^|\s+)([A-Za-z0-9_$])/g,
|
|
546
|
+
(_match, character) => character.toUpperCase()
|
|
547
|
+
).replace(/\s/g, "");
|
|
548
|
+
snapshot = uniqueLocalIdentifier(`_farmCurrentTarget${suffix}`, [
|
|
549
|
+
expression,
|
|
550
|
+
...snapshots.map((entry) => entry.identifier)
|
|
551
|
+
]);
|
|
552
|
+
snapshotsByProperty.set(snapshotProperty, snapshot);
|
|
553
|
+
snapshots.push({
|
|
554
|
+
identifier: snapshot,
|
|
555
|
+
value: types.cloneNode(propertyRead.node, true)
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
propertyRead.replaceWith(types.cloneNode(snapshot));
|
|
559
|
+
propertyRead.skip();
|
|
560
|
+
};
|
|
561
|
+
traverse(file, {
|
|
562
|
+
MemberExpression: replaceCurrentTarget,
|
|
563
|
+
OptionalMemberExpression: replaceCurrentTarget
|
|
564
|
+
});
|
|
565
|
+
if (snapshots.length === 0) return expression;
|
|
566
|
+
const declarations = snapshots.map(
|
|
567
|
+
({ identifier, value }) => types.variableDeclaration("const", [types.variableDeclarator(types.cloneNode(identifier), value)])
|
|
568
|
+
);
|
|
569
|
+
if (types.isBlockStatement(expression.body)) {
|
|
570
|
+
expression.body.body.unshift(...declarations);
|
|
571
|
+
} else {
|
|
572
|
+
expression.body = types.blockStatement([...declarations, types.returnStatement(expression.body)]);
|
|
573
|
+
}
|
|
574
|
+
return expression;
|
|
575
|
+
}
|
|
576
|
+
function stabilizeDeferredEventCurrentTargets(root, compilerSetters) {
|
|
577
|
+
const file = expressionFile(types.cloneNode(root, true));
|
|
578
|
+
traverse(file, {
|
|
579
|
+
JSXAttribute(path) {
|
|
580
|
+
if (!types.isJSXIdentifier(path.node.name) || !/^on[A-Z]/.test(path.node.name.name)) return;
|
|
581
|
+
const value = path.node.value;
|
|
582
|
+
if (!types.isJSXExpressionContainer(value) || !types.isArrowFunctionExpression(value.expression) && !types.isFunctionExpression(value.expression)) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
value.expression = stabilizeDeferredEventCurrentTarget(value.expression, compilerSetters);
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
const expression = file.program.body[0].expression;
|
|
589
|
+
return types.isJSXElement(expression) ? expression : types.cloneNode(root, true);
|
|
590
|
+
}
|
|
591
|
+
function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters) {
|
|
487
592
|
if (renderCallback.async || renderCallback.generator || renderCallback.params.length < 1 || renderCallback.params.length > 2 || !types.isIdentifier(renderCallback.params[0]) || renderCallback.params[1] !== void 0 && !types.isIdentifier(renderCallback.params[1])) {
|
|
488
593
|
return { reason: "compiled keyed rows require item and optional index identifiers" };
|
|
489
594
|
}
|
|
@@ -499,8 +604,25 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals) {
|
|
|
499
604
|
const attributeNames = new Set(
|
|
500
605
|
element.openingElement.attributes.filter((attribute) => types.isJSXAttribute(attribute)).map(jsxAttributeName).filter((name) => name !== void 0)
|
|
501
606
|
);
|
|
502
|
-
|
|
503
|
-
|
|
607
|
+
const controlled = attributeNames.has("value") || attributeNames.has("checked");
|
|
608
|
+
if (attributeNames.has("contentEditable")) {
|
|
609
|
+
return "content-editable keyed rows require React ownership";
|
|
610
|
+
}
|
|
611
|
+
if (tag.name === "input" && controlled) {
|
|
612
|
+
const type = jsxAttribute(element, "type");
|
|
613
|
+
const staticType = staticJsxAttributeString(type);
|
|
614
|
+
if (type && staticType === void 0) {
|
|
615
|
+
return "controlled keyed row inputs require a static type";
|
|
616
|
+
}
|
|
617
|
+
if (attributeNames.has("value") && staticType?.toLowerCase() === "file") {
|
|
618
|
+
return "file inputs require React ownership";
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (tag.name === "textarea" && controlled && meaningfulJsxChildren(element).length > 0) {
|
|
622
|
+
return "controlled keyed row textareas cannot also use children";
|
|
623
|
+
}
|
|
624
|
+
if (tag.name === "select" && controlled && controlledSelectHasDynamicOptions(element)) {
|
|
625
|
+
return "controlled keyed row selects require static options";
|
|
504
626
|
}
|
|
505
627
|
for (const attribute of element.openingElement.attributes) {
|
|
506
628
|
if (types.isJSXSpreadAttribute(attribute)) {
|
|
@@ -531,7 +653,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals) {
|
|
|
531
653
|
id: events.length,
|
|
532
654
|
path: [...path],
|
|
533
655
|
name,
|
|
534
|
-
value:
|
|
656
|
+
value: stabilizeDeferredEventCurrentTarget(handler, compilerSetters)
|
|
535
657
|
});
|
|
536
658
|
continue;
|
|
537
659
|
}
|
|
@@ -645,26 +767,14 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals) {
|
|
|
645
767
|
const reason = visit(row, []);
|
|
646
768
|
return reason ? { reason } : { bindings, events, conditionals };
|
|
647
769
|
}
|
|
648
|
-
function
|
|
649
|
-
if (container.openingElement.attributes.some(
|
|
650
|
-
(attribute) => types.isJSXSpreadAttribute(attribute) || types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && (attribute.name.name === "ref" || attribute.name.name === "dangerouslySetInnerHTML")
|
|
651
|
-
)) {
|
|
652
|
-
return void 0;
|
|
653
|
-
}
|
|
654
|
-
for (const attribute of container.openingElement.attributes) {
|
|
655
|
-
if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && types.isJSXExpressionContainer(attribute.value) && !types.isJSXEmptyExpression(attribute.value.expression) && !/^on[A-Z]/.test(attribute.name.name) && collectStateDependencies(attribute.value.expression, statesByValue).length > 0) {
|
|
656
|
-
return void 0;
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
const children = meaningfulJsxChildren(container);
|
|
660
|
-
if (children.length !== 1) return void 0;
|
|
770
|
+
function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames) {
|
|
661
771
|
let collection;
|
|
662
772
|
let keyCallback;
|
|
663
773
|
let renderCallback;
|
|
664
774
|
let row;
|
|
665
775
|
let dependencies;
|
|
666
776
|
let syntax;
|
|
667
|
-
|
|
777
|
+
let source;
|
|
668
778
|
if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression) && isMapCall(child.expression)) {
|
|
669
779
|
const result = analyzeMapList(child.expression, statesByValue, safeGlobals);
|
|
670
780
|
if (result.reason) return void 0;
|
|
@@ -684,6 +794,7 @@ function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNa
|
|
|
684
794
|
row = returned;
|
|
685
795
|
dependencies = result.dependencies || [];
|
|
686
796
|
syntax = "map";
|
|
797
|
+
source = child.expression;
|
|
687
798
|
} else if (types.isJSXElement(child) && isPublicListElement(child, listNames)) {
|
|
688
799
|
const result = analyzePublicList(child, statesByValue, safeGlobals);
|
|
689
800
|
if (result.reason) return void 0;
|
|
@@ -701,12 +812,19 @@ function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNa
|
|
|
701
812
|
row = returned;
|
|
702
813
|
dependencies = result.dependencies || [];
|
|
703
814
|
syntax = "list";
|
|
815
|
+
source = child;
|
|
704
816
|
} else {
|
|
705
817
|
return void 0;
|
|
706
818
|
}
|
|
707
|
-
const rowAnalysis = analyzeKeyedRowTree(
|
|
819
|
+
const rowAnalysis = analyzeKeyedRowTree(
|
|
820
|
+
row,
|
|
821
|
+
renderCallback,
|
|
822
|
+
safeGlobals,
|
|
823
|
+
new Set([...statesByValue.values()].map((state) => state.setterName))
|
|
824
|
+
);
|
|
708
825
|
if (rowAnalysis.reason) return void 0;
|
|
709
826
|
return {
|
|
827
|
+
source,
|
|
710
828
|
collection,
|
|
711
829
|
keyCallback,
|
|
712
830
|
renderCallback,
|
|
@@ -718,6 +836,76 @@ function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNa
|
|
|
718
836
|
syntax
|
|
719
837
|
};
|
|
720
838
|
}
|
|
839
|
+
function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNames) {
|
|
840
|
+
if (container.openingElement.attributes.some(
|
|
841
|
+
(attribute) => types.isJSXSpreadAttribute(attribute) || types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && (attribute.name.name === "ref" || attribute.name.name === "dangerouslySetInnerHTML")
|
|
842
|
+
)) {
|
|
843
|
+
return void 0;
|
|
844
|
+
}
|
|
845
|
+
for (const attribute of container.openingElement.attributes) {
|
|
846
|
+
if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && types.isJSXExpressionContainer(attribute.value) && !types.isJSXEmptyExpression(attribute.value.expression) && !/^on[A-Z]/.test(attribute.name.name) && collectStateDependencies(attribute.value.expression, statesByValue).length > 0) {
|
|
847
|
+
return void 0;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
const children = meaningfulJsxChildren(container);
|
|
851
|
+
if (children.length !== 1) return void 0;
|
|
852
|
+
return analyzeKeyedRowChild(children[0], statesByValue, safeGlobals, listNames);
|
|
853
|
+
}
|
|
854
|
+
function isStaticRangeSibling(element, statesByValue, safeGlobals) {
|
|
855
|
+
for (const child of meaningfulJsxChildren(element)) {
|
|
856
|
+
if (types.isJSXFragment(child)) return false;
|
|
857
|
+
if (types.isJSXElement(child)) {
|
|
858
|
+
if (!isHostElement(child) || !isStaticRangeSibling(child, statesByValue, safeGlobals)) {
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression) && !isTextExpression(child.expression, statesByValue, safeGlobals)) {
|
|
864
|
+
return false;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return true;
|
|
868
|
+
}
|
|
869
|
+
function analyzeKeyedRangesContainer(container, statesByValue, safeGlobals, listNames, allowSingleRange = false) {
|
|
870
|
+
if (container.openingElement.attributes.some(
|
|
871
|
+
(attribute) => types.isJSXSpreadAttribute(attribute) || types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && (attribute.name.name === "ref" || attribute.name.name === "dangerouslySetInnerHTML")
|
|
872
|
+
)) {
|
|
873
|
+
return void 0;
|
|
874
|
+
}
|
|
875
|
+
const children = meaningfulJsxChildren(container);
|
|
876
|
+
if (children.length < (allowSingleRange ? 1 : 2)) return void 0;
|
|
877
|
+
const ranges = [];
|
|
878
|
+
const dependencies = /* @__PURE__ */ new Set();
|
|
879
|
+
let staticBefore = 0;
|
|
880
|
+
for (const child of children) {
|
|
881
|
+
const range = analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames);
|
|
882
|
+
if (range) {
|
|
883
|
+
if (range.events.length > 0 || range.conditionals.length > 0) return void 0;
|
|
884
|
+
ranges.push({
|
|
885
|
+
before: staticBefore,
|
|
886
|
+
source: range.source,
|
|
887
|
+
collection: range.collection,
|
|
888
|
+
keyCallback: range.keyCallback,
|
|
889
|
+
renderCallback: range.renderCallback,
|
|
890
|
+
row: range.row,
|
|
891
|
+
bindings: range.bindings,
|
|
892
|
+
syntax: range.syntax
|
|
893
|
+
});
|
|
894
|
+
for (const dependency of range.dependencies) dependencies.add(dependency);
|
|
895
|
+
staticBefore = 0;
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
if (!types.isJSXElement(child) || !isHostElement(child)) return void 0;
|
|
899
|
+
if (!isStaticRangeSibling(child, statesByValue, safeGlobals)) return void 0;
|
|
900
|
+
staticBefore += 1;
|
|
901
|
+
}
|
|
902
|
+
if (ranges.length === 0) return void 0;
|
|
903
|
+
return {
|
|
904
|
+
ranges,
|
|
905
|
+
trailing: staticBefore,
|
|
906
|
+
dependencies: [...dependencies].sort((left, right) => left - right)
|
|
907
|
+
};
|
|
908
|
+
}
|
|
721
909
|
function analyzeHostConditionalTree(branch, safeGlobals) {
|
|
722
910
|
const bindings = [];
|
|
723
911
|
const visit = (element, path) => {
|
|
@@ -860,6 +1048,60 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals)
|
|
|
860
1048
|
falsyBindings: falsyAnalysis.bindings || []
|
|
861
1049
|
};
|
|
862
1050
|
}
|
|
1051
|
+
function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals, allowSingleRange = false) {
|
|
1052
|
+
const containerName = container.openingElement.name;
|
|
1053
|
+
if (!types.isJSXIdentifier(containerName) || !/^[a-z]/.test(containerName.name) || containerName.name === "svg" || container.openingElement.attributes.some(
|
|
1054
|
+
(attribute) => types.isJSXSpreadAttribute(attribute) || types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && (attribute.name.name === "ref" || attribute.name.name === "dangerouslySetInnerHTML")
|
|
1055
|
+
)) {
|
|
1056
|
+
return void 0;
|
|
1057
|
+
}
|
|
1058
|
+
const children = meaningfulJsxChildren(container);
|
|
1059
|
+
if (children.length < (allowSingleRange ? 1 : 2)) return void 0;
|
|
1060
|
+
const ranges = [];
|
|
1061
|
+
const dependencies = /* @__PURE__ */ new Set();
|
|
1062
|
+
let staticBefore = 0;
|
|
1063
|
+
for (const child of children) {
|
|
1064
|
+
if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)) {
|
|
1065
|
+
const shape = conditionalBlockShape(child.expression);
|
|
1066
|
+
if (shape && !validateDerivedExpression(shape.test, safeGlobals)) {
|
|
1067
|
+
const truthyAnalysis = shape.truthy ? analyzeHostConditionalTree(shape.truthy, safeGlobals) : { bindings: [] };
|
|
1068
|
+
const falsyAnalysis = shape.falsy ? analyzeHostConditionalTree(shape.falsy, safeGlobals) : { bindings: [] };
|
|
1069
|
+
if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
|
|
1070
|
+
const rangeDependencies = new Set(collectStateDependencies(shape.test, statesByValue));
|
|
1071
|
+
for (const binding of [
|
|
1072
|
+
...truthyAnalysis.bindings || [],
|
|
1073
|
+
...falsyAnalysis.bindings || []
|
|
1074
|
+
]) {
|
|
1075
|
+
for (const dependency of collectStateDependencies(binding.value, statesByValue)) {
|
|
1076
|
+
rangeDependencies.add(dependency);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
for (const dependency of rangeDependencies) dependencies.add(dependency);
|
|
1080
|
+
ranges.push({
|
|
1081
|
+
before: staticBefore,
|
|
1082
|
+
source: child.expression,
|
|
1083
|
+
test: cloneExpression(shape.test),
|
|
1084
|
+
logical: shape.logical,
|
|
1085
|
+
truthy: shape.truthy,
|
|
1086
|
+
falsy: shape.falsy,
|
|
1087
|
+
truthyBindings: truthyAnalysis.bindings || [],
|
|
1088
|
+
falsyBindings: falsyAnalysis.bindings || []
|
|
1089
|
+
});
|
|
1090
|
+
staticBefore = 0;
|
|
1091
|
+
continue;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
if (!types.isJSXElement(child) || !isHostElement(child)) return void 0;
|
|
1095
|
+
if (!isStaticRangeSibling(child, statesByValue, safeGlobals)) return void 0;
|
|
1096
|
+
staticBefore += 1;
|
|
1097
|
+
}
|
|
1098
|
+
if (ranges.length === 0) return void 0;
|
|
1099
|
+
return {
|
|
1100
|
+
ranges,
|
|
1101
|
+
trailing: staticBefore,
|
|
1102
|
+
dependencies: [...dependencies].sort((left, right) => left - right)
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
863
1105
|
function keyedListBoundary(blockRuntime, plan, source) {
|
|
864
1106
|
const name = types.jsxMemberExpression(
|
|
865
1107
|
types.jsxIdentifier(blockRuntime.name),
|
|
@@ -1207,6 +1449,74 @@ function keyedRowsBoundary(blockRuntime, plan) {
|
|
|
1207
1449
|
true
|
|
1208
1450
|
);
|
|
1209
1451
|
}
|
|
1452
|
+
function keyedRangeObject(range) {
|
|
1453
|
+
const parameters = range.renderCallback.params.map(
|
|
1454
|
+
(parameter) => types.cloneNode(parameter, true)
|
|
1455
|
+
);
|
|
1456
|
+
return types.objectExpression([
|
|
1457
|
+
types.objectProperty(types.identifier("before"), types.numericLiteral(range.before)),
|
|
1458
|
+
types.objectProperty(
|
|
1459
|
+
types.identifier("items"),
|
|
1460
|
+
types.arrowFunctionExpression([], cloneExpression(range.collection))
|
|
1461
|
+
),
|
|
1462
|
+
types.objectProperty(types.identifier("rowKey"), types.cloneNode(range.keyCallback, true)),
|
|
1463
|
+
types.objectProperty(
|
|
1464
|
+
types.identifier("create"),
|
|
1465
|
+
types.arrowFunctionExpression(
|
|
1466
|
+
parameters.map((parameter) => types.cloneNode(parameter, true)),
|
|
1467
|
+
hostElementDescriptor(range.row)
|
|
1468
|
+
)
|
|
1469
|
+
),
|
|
1470
|
+
types.objectProperty(
|
|
1471
|
+
types.identifier("bindings"),
|
|
1472
|
+
types.arrayExpression(
|
|
1473
|
+
range.bindings.map((binding) => keyedRowBindingObject(binding, parameters))
|
|
1474
|
+
)
|
|
1475
|
+
)
|
|
1476
|
+
]);
|
|
1477
|
+
}
|
|
1478
|
+
function keyedRangesBoundary(blockRuntime, plan) {
|
|
1479
|
+
const name = types.jsxMemberExpression(
|
|
1480
|
+
types.jsxIdentifier(blockRuntime.name),
|
|
1481
|
+
types.jsxIdentifier("KeyedRanges")
|
|
1482
|
+
);
|
|
1483
|
+
const source = types.cloneNode(plan.source, true);
|
|
1484
|
+
let rootRef;
|
|
1485
|
+
source.openingElement.attributes = source.openingElement.attributes.filter((attribute) => {
|
|
1486
|
+
if (!types.isJSXAttribute(attribute) || jsxAttributeName(attribute) !== "ref" || !types.isJSXExpressionContainer(attribute.value) || types.isJSXEmptyExpression(attribute.value.expression)) {
|
|
1487
|
+
return true;
|
|
1488
|
+
}
|
|
1489
|
+
rootRef = cloneExpression(attribute.value.expression);
|
|
1490
|
+
return false;
|
|
1491
|
+
});
|
|
1492
|
+
return types.jsxElement(
|
|
1493
|
+
types.jsxOpeningElement(
|
|
1494
|
+
name,
|
|
1495
|
+
[
|
|
1496
|
+
types.jsxAttribute(types.jsxIdentifier("id"), types.jsxExpressionContainer(types.numericLiteral(plan.id))),
|
|
1497
|
+
types.jsxAttribute(
|
|
1498
|
+
types.jsxIdentifier("render"),
|
|
1499
|
+
types.jsxExpressionContainer(types.arrowFunctionExpression([], source))
|
|
1500
|
+
),
|
|
1501
|
+
...rootRef ? [types.jsxAttribute(types.jsxIdentifier("rootRef"), types.jsxExpressionContainer(rootRef))] : [],
|
|
1502
|
+
types.jsxAttribute(
|
|
1503
|
+
types.jsxIdentifier("ranges"),
|
|
1504
|
+
types.jsxExpressionContainer(
|
|
1505
|
+
types.arrayExpression(plan.ranges.map((range) => keyedRangeObject(range)))
|
|
1506
|
+
)
|
|
1507
|
+
),
|
|
1508
|
+
types.jsxAttribute(
|
|
1509
|
+
types.jsxIdentifier("trailing"),
|
|
1510
|
+
types.jsxExpressionContainer(types.numericLiteral(plan.trailing))
|
|
1511
|
+
)
|
|
1512
|
+
],
|
|
1513
|
+
true
|
|
1514
|
+
),
|
|
1515
|
+
null,
|
|
1516
|
+
[],
|
|
1517
|
+
true
|
|
1518
|
+
);
|
|
1519
|
+
}
|
|
1210
1520
|
function hostConditionalBranchObject(branch, bindings) {
|
|
1211
1521
|
return types.objectExpression([
|
|
1212
1522
|
types.objectProperty(
|
|
@@ -1254,6 +1564,67 @@ function hostConditionalBoundary(blockRuntime, plan) {
|
|
|
1254
1564
|
}
|
|
1255
1565
|
return types.jsxElement(types.jsxOpeningElement(name, attributes, true), null, [], true);
|
|
1256
1566
|
}
|
|
1567
|
+
function conditionalRangeObject(range) {
|
|
1568
|
+
const properties = [
|
|
1569
|
+
types.objectProperty(types.identifier("before"), types.numericLiteral(range.before)),
|
|
1570
|
+
types.objectProperty(
|
|
1571
|
+
types.identifier("test"),
|
|
1572
|
+
types.arrowFunctionExpression([], cloneExpression(range.test))
|
|
1573
|
+
)
|
|
1574
|
+
];
|
|
1575
|
+
if (range.logical) {
|
|
1576
|
+
properties.push(types.objectProperty(types.identifier("logical"), types.booleanLiteral(true)));
|
|
1577
|
+
}
|
|
1578
|
+
if (range.truthy) {
|
|
1579
|
+
properties.push(
|
|
1580
|
+
types.objectProperty(
|
|
1581
|
+
types.identifier("truthy"),
|
|
1582
|
+
hostConditionalBranchObject(range.truthy, range.truthyBindings)
|
|
1583
|
+
)
|
|
1584
|
+
);
|
|
1585
|
+
}
|
|
1586
|
+
if (range.falsy) {
|
|
1587
|
+
properties.push(
|
|
1588
|
+
types.objectProperty(
|
|
1589
|
+
types.identifier("falsy"),
|
|
1590
|
+
hostConditionalBranchObject(range.falsy, range.falsyBindings)
|
|
1591
|
+
)
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
return types.objectExpression(properties);
|
|
1595
|
+
}
|
|
1596
|
+
function conditionalRangesBoundary(blockRuntime, plan) {
|
|
1597
|
+
const name = types.jsxMemberExpression(
|
|
1598
|
+
types.jsxIdentifier(blockRuntime.name),
|
|
1599
|
+
types.jsxIdentifier("ConditionalRanges")
|
|
1600
|
+
);
|
|
1601
|
+
const source = types.cloneNode(plan.source, true);
|
|
1602
|
+
const rootRefIndex = source.openingElement.attributes.findIndex(
|
|
1603
|
+
(attribute) => types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && attribute.name.name === "ref"
|
|
1604
|
+
);
|
|
1605
|
+
const rootRef = rootRefIndex >= 0 ? source.openingElement.attributes.splice(rootRefIndex, 1)[0] : void 0;
|
|
1606
|
+
const attributes = [
|
|
1607
|
+
types.jsxAttribute(types.jsxIdentifier("id"), types.jsxExpressionContainer(types.numericLiteral(plan.id))),
|
|
1608
|
+
types.jsxAttribute(
|
|
1609
|
+
types.jsxIdentifier("render"),
|
|
1610
|
+
types.jsxExpressionContainer(types.arrowFunctionExpression([], source))
|
|
1611
|
+
),
|
|
1612
|
+
types.jsxAttribute(
|
|
1613
|
+
types.jsxIdentifier("ranges"),
|
|
1614
|
+
types.jsxExpressionContainer(
|
|
1615
|
+
types.arrayExpression(plan.ranges.map((range) => conditionalRangeObject(range)))
|
|
1616
|
+
)
|
|
1617
|
+
),
|
|
1618
|
+
types.jsxAttribute(
|
|
1619
|
+
types.jsxIdentifier("trailing"),
|
|
1620
|
+
types.jsxExpressionContainer(types.numericLiteral(plan.trailing))
|
|
1621
|
+
)
|
|
1622
|
+
];
|
|
1623
|
+
if (rootRef?.value) {
|
|
1624
|
+
attributes.push(types.jsxAttribute(types.jsxIdentifier("rootRef"), types.cloneNode(rootRef.value, true)));
|
|
1625
|
+
}
|
|
1626
|
+
return types.jsxElement(types.jsxOpeningElement(name, attributes, true), null, [], true);
|
|
1627
|
+
}
|
|
1257
1628
|
function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allowedComponentNames) {
|
|
1258
1629
|
const name = element.openingElement.name;
|
|
1259
1630
|
if (!types.isJSXIdentifier(name) || !isComponentName(name.name)) {
|
|
@@ -1419,6 +1790,39 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
|
|
|
1419
1790
|
}
|
|
1420
1791
|
if (types.isJSXElement(child)) {
|
|
1421
1792
|
if (isHostElement(child)) {
|
|
1793
|
+
const keyedRanges = insideConditional ? void 0 : analyzeKeyedRangesContainer(child, statesByValue, safeGlobals, listNames);
|
|
1794
|
+
if (keyedRanges) {
|
|
1795
|
+
plans.push({
|
|
1796
|
+
kind: "keyed-ranges",
|
|
1797
|
+
id: nextId++,
|
|
1798
|
+
parent,
|
|
1799
|
+
dependencies: keyedRanges.dependencies,
|
|
1800
|
+
source: child,
|
|
1801
|
+
ranges: keyedRanges.ranges,
|
|
1802
|
+
trailing: keyedRanges.trailing
|
|
1803
|
+
});
|
|
1804
|
+
for (const range of keyedRanges.ranges) {
|
|
1805
|
+
if (types.isJSXElement(range.source)) ownedElements.add(range.source);
|
|
1806
|
+
else keyedExpressions.add(range.source);
|
|
1807
|
+
}
|
|
1808
|
+
continue;
|
|
1809
|
+
}
|
|
1810
|
+
const conditionalRanges = insideConditional ? void 0 : analyzeConditionalRangesContainer(child, statesByValue, safeGlobals);
|
|
1811
|
+
if (conditionalRanges) {
|
|
1812
|
+
plans.push({
|
|
1813
|
+
kind: "conditional-ranges",
|
|
1814
|
+
id: nextId++,
|
|
1815
|
+
parent,
|
|
1816
|
+
dependencies: conditionalRanges.dependencies,
|
|
1817
|
+
source: child,
|
|
1818
|
+
ranges: conditionalRanges.ranges,
|
|
1819
|
+
trailing: conditionalRanges.trailing
|
|
1820
|
+
});
|
|
1821
|
+
for (const range of conditionalRanges.ranges) {
|
|
1822
|
+
conditionalExpressions.add(range.source);
|
|
1823
|
+
}
|
|
1824
|
+
continue;
|
|
1825
|
+
}
|
|
1422
1826
|
const hostConditional = analyzeHostConditionalContainer(
|
|
1423
1827
|
child,
|
|
1424
1828
|
statesByValue,
|
|
@@ -1551,6 +1955,54 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
|
|
|
1551
1955
|
}
|
|
1552
1956
|
return { dependencies };
|
|
1553
1957
|
};
|
|
1958
|
+
const rootRanges = analyzeKeyedRangesContainer(root, statesByValue, safeGlobals, listNames, true);
|
|
1959
|
+
if (rootRanges) {
|
|
1960
|
+
plans.push({
|
|
1961
|
+
kind: "keyed-ranges",
|
|
1962
|
+
id: nextId++,
|
|
1963
|
+
dependencies: rootRanges.dependencies,
|
|
1964
|
+
source: root,
|
|
1965
|
+
ranges: rootRanges.ranges,
|
|
1966
|
+
trailing: rootRanges.trailing
|
|
1967
|
+
});
|
|
1968
|
+
for (const range of rootRanges.ranges) {
|
|
1969
|
+
if (types.isJSXElement(range.source)) ownedElements.add(range.source);
|
|
1970
|
+
else keyedExpressions.add(range.source);
|
|
1971
|
+
}
|
|
1972
|
+
return {
|
|
1973
|
+
plans,
|
|
1974
|
+
componentElements,
|
|
1975
|
+
conditionalExpressions,
|
|
1976
|
+
ownedElements,
|
|
1977
|
+
keyedExpressions
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
const rootConditionalRanges = analyzeConditionalRangesContainer(
|
|
1981
|
+
root,
|
|
1982
|
+
statesByValue,
|
|
1983
|
+
safeGlobals,
|
|
1984
|
+
true
|
|
1985
|
+
);
|
|
1986
|
+
if (rootConditionalRanges) {
|
|
1987
|
+
plans.push({
|
|
1988
|
+
kind: "conditional-ranges",
|
|
1989
|
+
id: nextId++,
|
|
1990
|
+
dependencies: rootConditionalRanges.dependencies,
|
|
1991
|
+
source: root,
|
|
1992
|
+
ranges: rootConditionalRanges.ranges,
|
|
1993
|
+
trailing: rootConditionalRanges.trailing
|
|
1994
|
+
});
|
|
1995
|
+
for (const range of rootConditionalRanges.ranges) {
|
|
1996
|
+
conditionalExpressions.add(range.source);
|
|
1997
|
+
}
|
|
1998
|
+
return {
|
|
1999
|
+
plans,
|
|
2000
|
+
componentElements,
|
|
2001
|
+
conditionalExpressions,
|
|
2002
|
+
ownedElements,
|
|
2003
|
+
keyedExpressions
|
|
2004
|
+
};
|
|
2005
|
+
}
|
|
1554
2006
|
const result = visitHost(root, void 0, false);
|
|
1555
2007
|
if (result.reason) return { reason: result.reason };
|
|
1556
2008
|
return {
|
|
@@ -1587,6 +2039,13 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
|
|
|
1587
2039
|
const planBySource = new Map(
|
|
1588
2040
|
plans.map((plan) => [plan.source, plan])
|
|
1589
2041
|
);
|
|
2042
|
+
const rootPlan = planBySource.get(root);
|
|
2043
|
+
if (rootPlan?.kind === "keyed-ranges") {
|
|
2044
|
+
return keyedRangesBoundary(blockRuntime, rootPlan);
|
|
2045
|
+
}
|
|
2046
|
+
if (rootPlan?.kind === "conditional-ranges") {
|
|
2047
|
+
return conditionalRangesBoundary(blockRuntime, rootPlan);
|
|
2048
|
+
}
|
|
1590
2049
|
const visit = (element) => {
|
|
1591
2050
|
element.children = element.children.map((child) => {
|
|
1592
2051
|
if (types.isJSXElement(child)) {
|
|
@@ -1597,6 +2056,12 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
|
|
|
1597
2056
|
if (plan2?.kind === "keyed-rows") {
|
|
1598
2057
|
return keyedRowsBoundary(blockRuntime, plan2);
|
|
1599
2058
|
}
|
|
2059
|
+
if (plan2?.kind === "keyed-ranges") {
|
|
2060
|
+
return keyedRangesBoundary(blockRuntime, plan2);
|
|
2061
|
+
}
|
|
2062
|
+
if (plan2?.kind === "conditional-ranges") {
|
|
2063
|
+
return conditionalRangesBoundary(blockRuntime, plan2);
|
|
2064
|
+
}
|
|
1600
2065
|
if (plan2?.kind === "component") {
|
|
1601
2066
|
return componentIslandBoundary(blockRuntime, plan2, child);
|
|
1602
2067
|
}
|
|
@@ -2030,11 +2495,15 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
|
|
|
2030
2495
|
const handlerRewrite = rewriteHandlerAccess(rootWithDerived, handlersByName);
|
|
2031
2496
|
if (handlerRewrite.reason) return handlerRewrite.reason;
|
|
2032
2497
|
if (!handlerRewrite.root) return "event handlers could not be prepared safely";
|
|
2033
|
-
const
|
|
2498
|
+
const expandedRootWithDeferredEvents = rewriteDestructuredPropAccess(
|
|
2034
2499
|
handlerRewrite.root,
|
|
2035
2500
|
propsPlan.destructuredNames,
|
|
2036
2501
|
propsPlan.definitionParameter
|
|
2037
2502
|
);
|
|
2503
|
+
const expandedRoot = stabilizeDeferredEventCurrentTargets(
|
|
2504
|
+
expandedRootWithDeferredEvents,
|
|
2505
|
+
new Set(states.map((state) => state.setterName))
|
|
2506
|
+
);
|
|
2038
2507
|
const setterReason = validateSetterUsage(expandedRoot, statesBySetter);
|
|
2039
2508
|
if (setterReason) return setterReason;
|
|
2040
2509
|
const referencedComponentNames = /* @__PURE__ */ new Set();
|