modality-ts 0.0.2 → 0.0.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/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +144 -18
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/pipeline/index.js +1 -1
- package/dist/extraction/pipeline/index.js.map +1 -1
- package/dist/modality/features/extract/command.d.ts.map +1 -1
- package/dist/modality/features/extract/command.js +71 -3
- package/dist/modality/features/extract/command.js.map +1 -1
- package/dist/sources/jotai/index.d.ts.map +1 -1
- package/dist/sources/jotai/index.js +32 -9
- package/dist/sources/jotai/index.js.map +1 -1
- package/dist/sources/swr/index.d.ts +2 -1
- package/dist/sources/swr/index.d.ts.map +1 -1
- package/dist/sources/swr/index.js +72 -9
- package/dist/sources/swr/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extraction/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,KAAK,EAAE,cAAc,EAA6B,YAAY,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACrH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAE/B,MAAM,WAAW,yBAAyB;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;IAClE,SAAS,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IACpC,aAAa,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,YAAY,EAAE,CAAC;IACrB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,sBAAuB,SAAQ,wBAAwB;IACtE,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AA0CD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,GAAG,SAAS,EAAE,WAAW,GAAE,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAa,GAAG,cAAc,CAuBhJ;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,yBAA8B,GAAG,wBAAwB,CAEzH;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,yBAA8B,GAAG,sBAAsB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extraction/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,KAAK,EAAE,cAAc,EAA6B,YAAY,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACrH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAE/B,MAAM,WAAW,yBAAyB;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;IAClE,SAAS,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IACpC,aAAa,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,YAAY,EAAE,CAAC;IACrB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,sBAAuB,SAAQ,wBAAwB;IACtE,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AA0CD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,GAAG,SAAS,EAAE,WAAW,GAAE,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAa,GAAG,cAAc,CAuBhJ;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,yBAA8B,GAAG,wBAAwB,CAEzH;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,yBAA8B,GAAG,sBAAsB,CA4I3H"}
|
package/dist/extraction/index.js
CHANGED
|
@@ -68,8 +68,10 @@ export function extractUseStateSkeleton(sourceText, options = {}) {
|
|
|
68
68
|
if (!componentName && isCustomHookDeclaration(node))
|
|
69
69
|
return;
|
|
70
70
|
const nextComponent = componentNameFor(node) ?? componentName;
|
|
71
|
-
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer
|
|
72
|
-
|
|
71
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {
|
|
72
|
+
const handler = extractableHandlerInitializer(node.initializer);
|
|
73
|
+
if (handler)
|
|
74
|
+
handlers.set(node.name.text, handler);
|
|
73
75
|
}
|
|
74
76
|
if (ts.isVariableDeclaration(node) && node.initializer && isUseReducerCall(node.initializer)) {
|
|
75
77
|
warnings.push({ message: `Unsupported useReducer ${nextComponent ?? "Anonymous"}.useReducer`, ...lineAndColumn(source, node) });
|
|
@@ -391,6 +393,15 @@ function isUseEffectCall(node) {
|
|
|
391
393
|
function isExtractableHandler(node) {
|
|
392
394
|
return ts.isArrowFunction(node) || ts.isFunctionExpression(node);
|
|
393
395
|
}
|
|
396
|
+
function extractableHandlerInitializer(node) {
|
|
397
|
+
if (isExtractableHandler(node))
|
|
398
|
+
return node;
|
|
399
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "useCallback") {
|
|
400
|
+
const callback = node.arguments[0];
|
|
401
|
+
return callback && isExtractableHandler(callback) ? callback : undefined;
|
|
402
|
+
}
|
|
403
|
+
return undefined;
|
|
404
|
+
}
|
|
394
405
|
function refSetterTaint(node, setters) {
|
|
395
406
|
if (ts.isVariableDeclaration(node) && node.initializer && isUseRefCall(node.initializer)) {
|
|
396
407
|
const arg = node.initializer.arguments[0];
|
|
@@ -594,7 +605,7 @@ function transitionsFromResolvedHandler(source, fileName, node, attr, handler, s
|
|
|
594
605
|
const loopTransitions = loopWriteTransitions(source, fileName, node, attr, handler, setters, component, locator);
|
|
595
606
|
if (loopTransitions.length > 0)
|
|
596
607
|
return applyParsedGuard(loopTransitions, disabledGuard);
|
|
597
|
-
const sequentialTransition = sequentialTransitionFromHandler(source, fileName, node, attr, handler, setters, component, locator);
|
|
608
|
+
const sequentialTransition = sequentialTransitionFromHandler(source, fileName, node, attr, handler, setters, handlers, component, locator);
|
|
598
609
|
if (sequentialTransition)
|
|
599
610
|
return applyParsedGuard([sequentialTransition], disabledGuard);
|
|
600
611
|
const summary = callSummaryFromHandler(handler, setters);
|
|
@@ -606,6 +617,9 @@ function transitionsFromResolvedHandler(source, fileName, node, attr, handler, s
|
|
|
606
617
|
const navigation = navigationTransition(source, fileName, node, attr, component, inlinedCall, locator);
|
|
607
618
|
if (navigation)
|
|
608
619
|
return applyParsedGuard([navigation], disabledGuard);
|
|
620
|
+
const swrMutate = swrMutateTransition(source, fileName, node, attr, component, inlinedCall, locator);
|
|
621
|
+
if (swrMutate)
|
|
622
|
+
return applyParsedGuard([swrMutate], disabledGuard);
|
|
609
623
|
const setterCall = setterCallFrom(inlinedCall, setters);
|
|
610
624
|
if (!setterCall) {
|
|
611
625
|
const escaped = escapedSetters(inlinedCall, setters);
|
|
@@ -633,7 +647,7 @@ function transitionsFromResolvedHandler(source, fileName, node, attr, handler, s
|
|
|
633
647
|
confidence: "exact"
|
|
634
648
|
}], disabledGuard);
|
|
635
649
|
}
|
|
636
|
-
function sequentialTransitionFromHandler(source, fileName, node, attr, handler, setters, component, locator) {
|
|
650
|
+
function sequentialTransitionFromHandler(source, fileName, node, attr, handler, setters, handlers, component, locator) {
|
|
637
651
|
if (!ts.isBlock(handler.body))
|
|
638
652
|
return undefined;
|
|
639
653
|
const locals = new Map();
|
|
@@ -641,6 +655,11 @@ function sequentialTransitionFromHandler(source, fileName, node, attr, handler,
|
|
|
641
655
|
for (const statement of handler.body.statements) {
|
|
642
656
|
if (bindConstStatement(statement, setters, locals))
|
|
643
657
|
continue;
|
|
658
|
+
const helper = helperSummariesFromStatement(statement, handlers, setters);
|
|
659
|
+
if (helper) {
|
|
660
|
+
summaries.push(...helper);
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
644
663
|
const summary = summarizeSetterStatement(statement, setters, locals);
|
|
645
664
|
if (!summary)
|
|
646
665
|
return undefined;
|
|
@@ -662,6 +681,23 @@ function sequentialTransitionFromHandler(source, fileName, node, attr, handler,
|
|
|
662
681
|
confidence: effects.some((effect) => effect.kind === "havoc") ? "over-approx" : "exact"
|
|
663
682
|
};
|
|
664
683
|
}
|
|
684
|
+
function helperSummariesFromStatement(statement, handlers, setters) {
|
|
685
|
+
if (!ts.isExpressionStatement(statement) || !ts.isCallExpression(statement.expression) || !ts.isIdentifier(statement.expression.expression))
|
|
686
|
+
return undefined;
|
|
687
|
+
const helper = handlers.get(statement.expression.expression.text);
|
|
688
|
+
if (!helper || !ts.isBlock(helper.body))
|
|
689
|
+
return undefined;
|
|
690
|
+
const locals = new Map();
|
|
691
|
+
const summaries = [];
|
|
692
|
+
for (const child of helper.body.statements) {
|
|
693
|
+
if (bindConstStatement(child, setters, locals))
|
|
694
|
+
continue;
|
|
695
|
+
const summary = summarizeSetterStatement(child, setters, locals);
|
|
696
|
+
if (summary)
|
|
697
|
+
summaries.push(summary);
|
|
698
|
+
}
|
|
699
|
+
return summaries.length > 0 ? summaries : undefined;
|
|
700
|
+
}
|
|
665
701
|
function loopWriteTransitions(source, fileName, node, attr, handler, setters, component, locator) {
|
|
666
702
|
if (!ts.isBlock(handler.body))
|
|
667
703
|
return [];
|
|
@@ -727,6 +763,11 @@ function havocSetterTransition(source, fileName, node, attr, component, setter,
|
|
|
727
763
|
};
|
|
728
764
|
}
|
|
729
765
|
function setterArgumentExpr(argument, setter, setters, locals) {
|
|
766
|
+
if (ts.isObjectLiteralExpression(argument)) {
|
|
767
|
+
const object = objectLiteralAssignmentExpr(argument, setter.domain, setters, locals);
|
|
768
|
+
if (object)
|
|
769
|
+
return object;
|
|
770
|
+
}
|
|
730
771
|
if ((ts.isArrowFunction(argument) || ts.isFunctionExpression(argument)) && argument.parameters.length === 1 && ts.isIdentifier(argument.parameters[0].name)) {
|
|
731
772
|
if (ts.isBlock(argument.body))
|
|
732
773
|
return undefined;
|
|
@@ -734,11 +775,42 @@ function setterArgumentExpr(argument, setter, setters, locals) {
|
|
|
734
775
|
}
|
|
735
776
|
return valueExpr(argument, setters, locals);
|
|
736
777
|
}
|
|
778
|
+
function objectLiteralAssignmentExpr(expression, domain, setters, locals) {
|
|
779
|
+
const value = {};
|
|
780
|
+
const reads = new Set();
|
|
781
|
+
const fields = domain.kind === "record" ? domain.fields : domain.kind === "tagged" ? taggedFieldsForObject(expression, domain) : {};
|
|
782
|
+
for (const property of expression.properties) {
|
|
783
|
+
if (!ts.isPropertyAssignment(property))
|
|
784
|
+
return undefined;
|
|
785
|
+
const name = propertyName(property.name);
|
|
786
|
+
if (!name)
|
|
787
|
+
return undefined;
|
|
788
|
+
const literal = literalValue(property.initializer);
|
|
789
|
+
if (literal !== undefined) {
|
|
790
|
+
value[name] = literal;
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
const bound = valueExpr(property.initializer, setters, locals);
|
|
794
|
+
if (bound?.expr.kind === "lit") {
|
|
795
|
+
value[name] = bound.expr.value;
|
|
796
|
+
bound.reads.forEach((read) => reads.add(read));
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
value[name] = firstValue(fields[name] ?? { kind: "tokens", count: 1 });
|
|
800
|
+
}
|
|
801
|
+
return { expr: { kind: "lit", value }, reads: [...reads] };
|
|
802
|
+
}
|
|
803
|
+
function taggedFieldsForObject(expression, domain) {
|
|
804
|
+
const tagProperty = expression.properties.find((property) => ts.isPropertyAssignment(property) && propertyName(property.name) === domain.tag);
|
|
805
|
+
const tag = tagProperty ? literalValue(tagProperty.initializer) : undefined;
|
|
806
|
+
const variant = typeof tag === "string" ? domain.variants[tag] : undefined;
|
|
807
|
+
return variant?.kind === "record" ? variant.fields : {};
|
|
808
|
+
}
|
|
737
809
|
function valueExpr(expression, setters, locals) {
|
|
738
810
|
const value = literalValue(expression);
|
|
739
811
|
if (value !== undefined)
|
|
740
812
|
return { expr: { kind: "lit", value }, reads: [] };
|
|
741
|
-
if (ts.isIdentifier(expression) ||
|
|
813
|
+
if (ts.isIdentifier(expression) || isPropertyAccessLike(expression))
|
|
742
814
|
return modeledReadExpr(expression, setters, locals);
|
|
743
815
|
if (ts.isPrefixUnaryExpression(expression) && expression.operator === ts.SyntaxKind.ExclamationToken) {
|
|
744
816
|
const parsed = booleanExpr(expression.operand, setters, locals);
|
|
@@ -747,7 +819,7 @@ function valueExpr(expression, setters, locals) {
|
|
|
747
819
|
if (ts.isParenthesizedExpression(expression))
|
|
748
820
|
return valueExpr(expression.expression, setters, locals);
|
|
749
821
|
if (ts.isBinaryExpression(expression) && expression.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
750
|
-
return nullishOptionalReadExpr(expression, setters);
|
|
822
|
+
return nullishOptionalReadExpr(expression, setters, locals);
|
|
751
823
|
}
|
|
752
824
|
if (ts.isConditionalExpression(expression)) {
|
|
753
825
|
const condition = booleanExpr(expression.condition, setters, locals);
|
|
@@ -791,22 +863,25 @@ function objectSpreadUpdateExpr(expression, setters, locals) {
|
|
|
791
863
|
}
|
|
792
864
|
return current;
|
|
793
865
|
}
|
|
794
|
-
function nullishOptionalReadExpr(expression, setters) {
|
|
795
|
-
|
|
866
|
+
function nullishOptionalReadExpr(expression, setters, locals = new Map()) {
|
|
867
|
+
const fallback = literalValue(expression.right);
|
|
868
|
+
if (fallback === undefined)
|
|
796
869
|
return undefined;
|
|
797
870
|
const read = optionalReadPath(expression.left);
|
|
798
871
|
if (!read?.optional || read.path.length === 0)
|
|
799
872
|
return undefined;
|
|
800
|
-
const
|
|
873
|
+
const local = locals.get(read.base);
|
|
874
|
+
const varId = local?.expr.kind === "read" ? local.expr.var : stateVarForName(read.base, setters);
|
|
801
875
|
if (!varId)
|
|
802
876
|
return undefined;
|
|
877
|
+
const basePath = local?.expr.kind === "read" ? local.expr.path ?? [] : [];
|
|
803
878
|
return {
|
|
804
879
|
expr: {
|
|
805
880
|
kind: "cond",
|
|
806
881
|
args: [
|
|
807
|
-
{ kind: "eq", args: [{ kind: "read", var: varId }, { kind: "lit", value: null }] },
|
|
808
|
-
{ kind: "lit", value:
|
|
809
|
-
{ kind: "read", var: varId, path: read.path }
|
|
882
|
+
{ kind: "eq", args: [{ kind: "read", var: varId, ...(basePath.length > 0 ? { path: basePath } : {}) }, { kind: "lit", value: null }] },
|
|
883
|
+
{ kind: "lit", value: fallback },
|
|
884
|
+
{ kind: "read", var: varId, path: [...basePath, ...read.path] }
|
|
810
885
|
]
|
|
811
886
|
},
|
|
812
887
|
reads: [varId]
|
|
@@ -815,7 +890,7 @@ function nullishOptionalReadExpr(expression, setters) {
|
|
|
815
890
|
function optionalReadPath(expression) {
|
|
816
891
|
if (ts.isIdentifier(expression))
|
|
817
892
|
return { base: expression.text, path: [], optional: false };
|
|
818
|
-
if (
|
|
893
|
+
if (isPropertyAccessLike(expression)) {
|
|
819
894
|
const base = optionalReadPath(expression.expression);
|
|
820
895
|
if (!base)
|
|
821
896
|
return undefined;
|
|
@@ -891,9 +966,13 @@ function modeledReadExpr(expression, setters, locals) {
|
|
|
891
966
|
reads: local.reads
|
|
892
967
|
};
|
|
893
968
|
}
|
|
894
|
-
const
|
|
969
|
+
const setter = setterForName(base, setters);
|
|
970
|
+
const stateVar = setter?.varId;
|
|
895
971
|
if (!stateVar)
|
|
896
972
|
return undefined;
|
|
973
|
+
if (setter.domain.kind === "tagged" && segments.length > 0 && segments[0] !== setter.domain.tag) {
|
|
974
|
+
return { expr: { kind: "lit", value: firstValue(taggedPathDomain(setter.domain, segments) ?? { kind: "tokens", count: 1 }) }, reads: [] };
|
|
975
|
+
}
|
|
897
976
|
return {
|
|
898
977
|
expr: { kind: "read", var: stateVar, ...(segments.length > 0 ? { path: segments } : {}) },
|
|
899
978
|
reads: [stateVar]
|
|
@@ -980,6 +1059,8 @@ function callSummaryFromHandler(handler, setters, initialLocals = new Map()) {
|
|
|
980
1059
|
const body = handler.body;
|
|
981
1060
|
if (ts.isCallExpression(body))
|
|
982
1061
|
return { call: body, locals: new Map(initialLocals) };
|
|
1062
|
+
if (ts.isVoidExpression(body) && ts.isCallExpression(body.expression))
|
|
1063
|
+
return { call: body.expression, locals: new Map(initialLocals) };
|
|
983
1064
|
if (ts.isBlock(body)) {
|
|
984
1065
|
const locals = new Map(initialLocals);
|
|
985
1066
|
for (let index = 0; index < body.statements.length; index += 1) {
|
|
@@ -987,12 +1068,29 @@ function callSummaryFromHandler(handler, setters, initialLocals = new Map()) {
|
|
|
987
1068
|
const isLast = index === body.statements.length - 1;
|
|
988
1069
|
if (isLast && ts.isExpressionStatement(statement) && ts.isCallExpression(statement.expression))
|
|
989
1070
|
return { call: statement.expression, locals };
|
|
1071
|
+
if (isLast && ts.isExpressionStatement(statement) && ts.isVoidExpression(statement.expression) && ts.isCallExpression(statement.expression.expression))
|
|
1072
|
+
return { call: statement.expression.expression, locals };
|
|
990
1073
|
if (!bindConstStatement(statement, setters, locals))
|
|
991
1074
|
return undefined;
|
|
992
1075
|
}
|
|
993
1076
|
}
|
|
994
1077
|
return undefined;
|
|
995
1078
|
}
|
|
1079
|
+
function swrMutateTransition(source, fileName, node, attr, component, call, locator) {
|
|
1080
|
+
if (!ts.isIdentifier(call.expression) || call.expression.text !== "mutate")
|
|
1081
|
+
return undefined;
|
|
1082
|
+
return {
|
|
1083
|
+
id: `${component}.${attr}.mutate`,
|
|
1084
|
+
cls: "user",
|
|
1085
|
+
label: labelForEvent(attr, locator),
|
|
1086
|
+
source: [{ file: fileName, ...lineAndColumn(source, node) }],
|
|
1087
|
+
guard: { kind: "lit", value: true },
|
|
1088
|
+
effect: { kind: "seq", effects: [] },
|
|
1089
|
+
reads: [],
|
|
1090
|
+
writes: [],
|
|
1091
|
+
confidence: "exact"
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
996
1094
|
function bindConstStatement(statement, setters, locals, partialBoolean = false) {
|
|
997
1095
|
if (!ts.isVariableStatement(statement))
|
|
998
1096
|
return false;
|
|
@@ -1989,7 +2087,7 @@ function parseGuardExpression(expression, setters, locals = new Map()) {
|
|
|
1989
2087
|
return { expr: { kind: "lit", value: true }, reads: [] };
|
|
1990
2088
|
if (expression.kind === ts.SyntaxKind.FalseKeyword)
|
|
1991
2089
|
return { expr: { kind: "lit", value: false }, reads: [] };
|
|
1992
|
-
if (ts.isIdentifier(expression) ||
|
|
2090
|
+
if (ts.isIdentifier(expression) || isPropertyAccessLike(expression))
|
|
1993
2091
|
return valueExpr(expression, setters, locals);
|
|
1994
2092
|
if (ts.isPrefixUnaryExpression(expression) && expression.operator === ts.SyntaxKind.ExclamationToken) {
|
|
1995
2093
|
const parsed = parseGuardExpression(expression.operand, setters, locals);
|
|
@@ -2034,7 +2132,7 @@ function parseGuardOperand(expression, setters, locals = new Map()) {
|
|
|
2034
2132
|
const value = literalValue(expression);
|
|
2035
2133
|
if (value !== undefined)
|
|
2036
2134
|
return { expr: { kind: "lit", value }, reads: [] };
|
|
2037
|
-
if (ts.isIdentifier(expression) ||
|
|
2135
|
+
if (ts.isIdentifier(expression) || isPropertyAccessLike(expression))
|
|
2038
2136
|
return valueExpr(expression, setters, locals);
|
|
2039
2137
|
return parseGuardExpression(expression, setters, locals);
|
|
2040
2138
|
}
|
|
@@ -2050,7 +2148,32 @@ function parseConjunctiveGuardExpression(expression, setters, locals = new Map()
|
|
|
2050
2148
|
return parseGuardExpression(expression, setters, locals);
|
|
2051
2149
|
}
|
|
2052
2150
|
function stateVarForName(name, setters) {
|
|
2053
|
-
return
|
|
2151
|
+
return setterForName(name, setters)?.varId;
|
|
2152
|
+
}
|
|
2153
|
+
function setterForName(name, setters) {
|
|
2154
|
+
return setters.get(name) ?? [...setters.values()].find((setter) => setter.stateName === name);
|
|
2155
|
+
}
|
|
2156
|
+
function taggedPathDomain(domain, path) {
|
|
2157
|
+
const [field, ...rest] = path;
|
|
2158
|
+
if (!field)
|
|
2159
|
+
return domain;
|
|
2160
|
+
const variants = Object.values(domain.variants).filter((variant) => variant.kind === "record");
|
|
2161
|
+
const fieldDomains = variants.map((variant) => variant.fields[field]).filter((candidate) => Boolean(candidate));
|
|
2162
|
+
if (fieldDomains.length === 0)
|
|
2163
|
+
return undefined;
|
|
2164
|
+
const first = fieldDomains[0];
|
|
2165
|
+
if (rest.length === 0)
|
|
2166
|
+
return first;
|
|
2167
|
+
return first.kind === "record" ? domainAtRecordPath(first, rest) : undefined;
|
|
2168
|
+
}
|
|
2169
|
+
function domainAtRecordPath(domain, path) {
|
|
2170
|
+
const [field, ...rest] = path;
|
|
2171
|
+
if (!field)
|
|
2172
|
+
return domain;
|
|
2173
|
+
const next = domain.fields[field];
|
|
2174
|
+
if (!next || rest.length === 0)
|
|
2175
|
+
return next;
|
|
2176
|
+
return next.kind === "record" ? domainAtRecordPath(next, rest) : undefined;
|
|
2054
2177
|
}
|
|
2055
2178
|
function andGuard(left, right) {
|
|
2056
2179
|
if (isTrueLiteral(left))
|
|
@@ -2149,12 +2272,15 @@ function isInputValueExpression(node, parameter) {
|
|
|
2149
2272
|
function propertyAccessPath(node) {
|
|
2150
2273
|
if (ts.isIdentifier(node))
|
|
2151
2274
|
return [node.text];
|
|
2152
|
-
if (
|
|
2275
|
+
if (isPropertyAccessLike(node)) {
|
|
2153
2276
|
const base = propertyAccessPath(node.expression);
|
|
2154
2277
|
return base ? [...base, node.name.text] : undefined;
|
|
2155
2278
|
}
|
|
2156
2279
|
return undefined;
|
|
2157
2280
|
}
|
|
2281
|
+
function isPropertyAccessLike(node) {
|
|
2282
|
+
return ts.isPropertyAccessExpression(node) || ts.isPropertyAccessChain(node);
|
|
2283
|
+
}
|
|
2158
2284
|
function valueClassForDomain(domain) {
|
|
2159
2285
|
if (domain.kind === "enum")
|
|
2160
2286
|
return domain.values.join("|") || "enum";
|