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