modality-ts 0.0.3 → 0.0.4
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/checker/search/eval.d.ts.map +1 -1
- package/dist/checker/search/eval.js +49 -1
- package/dist/checker/search/eval.js.map +1 -1
- package/dist/checker/search/index.js +26 -7
- package/dist/checker/search/index.js.map +1 -1
- package/dist/extraction/index.d.ts +4 -2
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +102 -21
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/pipeline/index.d.ts +2 -0
- package/dist/extraction/pipeline/index.d.ts.map +1 -1
- package/dist/extraction/pipeline/index.js +3 -1
- package/dist/extraction/pipeline/index.js.map +1 -1
- package/dist/harness/index.d.ts +29 -2
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +79 -9
- package/dist/harness/index.js.map +1 -1
- package/dist/kernel/overlay/index.d.ts +16 -1
- package/dist/kernel/overlay/index.d.ts.map +1 -1
- package/dist/kernel/overlay/index.js +56 -1
- package/dist/kernel/overlay/index.js.map +1 -1
- package/dist/kernel/report/types.d.ts +4 -0
- package/dist/kernel/report/types.d.ts.map +1 -1
- package/dist/modality/cli.js +15 -9
- package/dist/modality/cli.js.map +1 -1
- package/dist/modality/codegen/replay-test.d.ts.map +1 -1
- package/dist/modality/codegen/replay-test.js +13 -4
- package/dist/modality/codegen/replay-test.js.map +1 -1
- package/dist/modality/features/ci/command.d.ts +2 -0
- package/dist/modality/features/ci/command.d.ts.map +1 -1
- package/dist/modality/features/ci/command.js +2 -0
- package/dist/modality/features/ci/command.js.map +1 -1
- package/dist/modality/features/conform/command.d.ts +2 -0
- package/dist/modality/features/conform/command.d.ts.map +1 -1
- package/dist/modality/features/conform/command.js +91 -6
- package/dist/modality/features/conform/command.js.map +1 -1
- package/dist/modality/features/export/command.d.ts +29 -1
- package/dist/modality/features/export/command.d.ts.map +1 -1
- package/dist/modality/features/export/command.js +82 -1
- package/dist/modality/features/export/command.js.map +1 -1
- package/dist/modality/features/export/index.d.ts +2 -2
- package/dist/modality/features/export/index.d.ts.map +1 -1
- package/dist/modality/features/export/index.js +1 -1
- package/dist/modality/features/export/index.js.map +1 -1
- package/dist/modality/features/extract/command.js +4 -4
- package/dist/modality/features/extract/command.js.map +1 -1
- package/dist/modality/features/replay/command.d.ts +2 -0
- package/dist/modality/features/replay/command.d.ts.map +1 -1
- package/dist/modality/features/replay/command.js +89 -6
- package/dist/modality/features/replay/command.js.map +1 -1
- package/dist/modality/overlay.d.ts +2 -1
- package/dist/modality/overlay.d.ts.map +1 -1
- package/dist/modality/overlay.js +13 -2
- package/dist/modality/overlay.js.map +1 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +22 -2
- package/dist/runtime/index.js.map +1 -1
- package/dist/sources/swr/index.d.ts +3 -0
- package/dist/sources/swr/index.d.ts.map +1 -1
- package/dist/sources/swr/index.js +40 -3
- package/dist/sources/swr/index.js.map +1 -1
- package/package.json +2 -2
package/dist/extraction/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as ts from "typescript";
|
|
2
|
+
import { effectReads, effectWrites } from "modality-ts/kernel";
|
|
2
3
|
export * from "./pipeline/index.js";
|
|
3
4
|
export * from "./spi/index.js";
|
|
4
5
|
function setterBindingFromDecl(decl) {
|
|
@@ -48,6 +49,8 @@ export function extractUseStateSkeleton(sourceText, options = {}) {
|
|
|
48
49
|
const warnings = [];
|
|
49
50
|
const route = options.route ?? "/";
|
|
50
51
|
const effectApis = new Set(options.effectApis ?? []);
|
|
52
|
+
const sourcePlugins = options.sourcePlugins ?? [];
|
|
53
|
+
const routerPlugin = options.routerPlugin;
|
|
51
54
|
const setters = new Map();
|
|
52
55
|
const globalTaints = new Set();
|
|
53
56
|
const components = componentDeclarations(source);
|
|
@@ -137,7 +140,7 @@ export function extractUseStateSkeleton(sourceText, options = {}) {
|
|
|
137
140
|
}
|
|
138
141
|
}
|
|
139
142
|
if (ts.isJsxAttribute(node) && ts.isIdentifier(node.name) && node.initializer && isForwardablePropName(node.name.text) && !isIntrinsicJsxAttribute(node)) {
|
|
140
|
-
const extracted = transitionsFromComponentPropAttribute(source, fileName, node, setters, handlers, components, nextComponent ?? "Anonymous", effectApis, options.asyncOutcomes ?? {}, warnings);
|
|
143
|
+
const extracted = transitionsFromComponentPropAttribute(source, fileName, node, setters, handlers, components, nextComponent ?? "Anonymous", effectApis, options.asyncOutcomes ?? {}, sourcePlugins, routerPlugin, warnings);
|
|
141
144
|
transitions.push(...extracted);
|
|
142
145
|
if (extracted.length === 0) {
|
|
143
146
|
warnings.push({ message: `Unextractable handler ${nextComponent ?? "Anonymous"}.${node.name.text}`, ...lineAndColumn(source, node) });
|
|
@@ -167,7 +170,7 @@ export function extractUseStateSkeleton(sourceText, options = {}) {
|
|
|
167
170
|
renderGuardFor(node, setters, warnings, source, nextComponent ?? "Anonymous", guardLocals),
|
|
168
171
|
disabledGuardFor(node, setters, warnings, source, nextComponent ?? "Anonymous", guardLocals)
|
|
169
172
|
]);
|
|
170
|
-
const extracted = transitionsFromJsxAttribute(source, fileName, node, setters, handlers, nextComponent ?? "Anonymous", effectApis, options.asyncOutcomes ?? {}, guard, warnings);
|
|
173
|
+
const extracted = transitionsFromJsxAttribute(source, fileName, node, setters, handlers, nextComponent ?? "Anonymous", effectApis, options.asyncOutcomes ?? {}, sourcePlugins, routerPlugin, guard, warnings);
|
|
171
174
|
transitions.push(...extracted);
|
|
172
175
|
if (extracted.length === 0 && !forwardsComponentProp(node, handlers, components.get(nextComponent ?? "")) && !handlerSchedulesModeledTimer(node, handlers, setters)) {
|
|
173
176
|
warnings.push({ message: `Unextractable handler ${nextComponent ?? "Anonymous"}.${node.name.text}`, ...lineAndColumn(source, node) });
|
|
@@ -501,7 +504,7 @@ function handlerSchedulesModeledTimer(attribute, handlers, setters) {
|
|
|
501
504
|
visit(handler.body);
|
|
502
505
|
return found;
|
|
503
506
|
}
|
|
504
|
-
function transitionsFromJsxAttribute(source, fileName, node, setters, handlers, component, effectApis, asyncOutcomes, disabledGuard, warnings) {
|
|
507
|
+
function transitionsFromJsxAttribute(source, fileName, node, setters, handlers, component, effectApis, asyncOutcomes, sourcePlugins, routerPlugin, disabledGuard, warnings) {
|
|
505
508
|
if (!node.initializer)
|
|
506
509
|
return [];
|
|
507
510
|
const expression = ts.isJsxExpression(node.initializer) ? node.initializer.expression : undefined;
|
|
@@ -512,9 +515,9 @@ function transitionsFromJsxAttribute(source, fileName, node, setters, handlers,
|
|
|
512
515
|
return [];
|
|
513
516
|
const attr = node.name.text;
|
|
514
517
|
const locator = locatorForEventAttribute(node);
|
|
515
|
-
return tagStableIdKey(transitionsFromResolvedHandler(source, fileName, node, attr, handler, setters, handlers, component, effectApis, asyncOutcomes, disabledGuard, locator, warnings), handler);
|
|
518
|
+
return tagStableIdKey(transitionsFromResolvedHandler(source, fileName, node, attr, handler, setters, handlers, component, effectApis, asyncOutcomes, sourcePlugins, routerPlugin, disabledGuard, locator, warnings), handler);
|
|
516
519
|
}
|
|
517
|
-
function transitionsFromComponentPropAttribute(source, fileName, node, setters, handlers, components, component, effectApis, asyncOutcomes, warnings) {
|
|
520
|
+
function transitionsFromComponentPropAttribute(source, fileName, node, setters, handlers, components, component, effectApis, asyncOutcomes, sourcePlugins, routerPlugin, warnings) {
|
|
518
521
|
if (!node.initializer || !ts.isIdentifier(node.name))
|
|
519
522
|
return [];
|
|
520
523
|
const tag = jsxTagName(node);
|
|
@@ -535,7 +538,7 @@ function transitionsFromComponentPropAttribute(source, fileName, node, setters,
|
|
|
535
538
|
renderGuardFor(node, setters, warnings, source, component, guardLocals),
|
|
536
539
|
disabledGuardFor(node, setters, warnings, source, component, guardLocals)
|
|
537
540
|
]);
|
|
538
|
-
return tagStableIdKey(transitionsFromResolvedHandler(source, fileName, node, trigger.attr, handler, setters, handlers, component, effectApis, asyncOutcomes, combineParsedGuards([trigger.guard, callerGuard]), trigger.locator, warnings), handler);
|
|
541
|
+
return tagStableIdKey(transitionsFromResolvedHandler(source, fileName, node, trigger.attr, handler, setters, handlers, component, effectApis, asyncOutcomes, sourcePlugins, routerPlugin, combineParsedGuards([trigger.guard, callerGuard]), trigger.locator, warnings), handler);
|
|
539
542
|
}
|
|
540
543
|
function transitionsFromBoundedListAttribute(source, fileName, node, setters, handlers, component, listInfo) {
|
|
541
544
|
if (!node.initializer || !ts.isIdentifier(node.name))
|
|
@@ -595,7 +598,7 @@ function normalizedAstKey(node) {
|
|
|
595
598
|
.replace(/\/\/.*$/gm, "")
|
|
596
599
|
.replace(/\s+/g, "");
|
|
597
600
|
}
|
|
598
|
-
function transitionsFromResolvedHandler(source, fileName, node, attr, handler, setters, handlers, component, effectApis, asyncOutcomes, disabledGuard, locator, warnings) {
|
|
601
|
+
function transitionsFromResolvedHandler(source, fileName, node, attr, handler, setters, handlers, component, effectApis, asyncOutcomes, sourcePlugins, routerPlugin, disabledGuard, locator, warnings) {
|
|
599
602
|
const asyncTransitions = transitionsFromAsyncHandler(source, fileName, attr, handler, setters, component, effectApis, asyncOutcomes, locator, warnings);
|
|
600
603
|
if (asyncTransitions.length > 0)
|
|
601
604
|
return applyParsedGuard(asyncTransitions, disabledGuard);
|
|
@@ -614,15 +617,18 @@ function transitionsFromResolvedHandler(source, fileName, node, attr, handler, s
|
|
|
614
617
|
const inlined = inlinedHelperCall(summary.call, handlers, setters);
|
|
615
618
|
const inlinedCall = inlined?.call ?? summary.call;
|
|
616
619
|
const locals = inlined?.locals ?? summary.locals;
|
|
617
|
-
const navigation = navigationTransition(source, fileName, node, attr, component, inlinedCall, locator);
|
|
620
|
+
const navigation = navigationTransition(source, fileName, node, attr, component, inlinedCall, locator, routerPlugin);
|
|
618
621
|
if (navigation)
|
|
619
622
|
return applyParsedGuard([navigation], disabledGuard);
|
|
623
|
+
const pluginWrite = pluginWriteTransition(source, fileName, node, attr, component, inlinedCall, setters, locals, sourcePlugins, locator);
|
|
624
|
+
if (pluginWrite)
|
|
625
|
+
return applyParsedGuard([pluginWrite], disabledGuard);
|
|
620
626
|
const swrMutate = swrMutateTransition(source, fileName, node, attr, component, inlinedCall, locator);
|
|
621
627
|
if (swrMutate)
|
|
622
628
|
return applyParsedGuard([swrMutate], disabledGuard);
|
|
623
629
|
const setterCall = setterCallFrom(inlinedCall, setters);
|
|
624
630
|
if (!setterCall) {
|
|
625
|
-
const escaped = escapedSetters(inlinedCall, setters);
|
|
631
|
+
const escaped = escapedSetters(inlinedCall, setters, locals);
|
|
626
632
|
if (escaped.length === 0)
|
|
627
633
|
return [];
|
|
628
634
|
return applyParsedGuard(escapedSetterTransitions(source, fileName, node, attr, component, escaped, locator), disabledGuard);
|
|
@@ -818,6 +824,10 @@ function valueExpr(expression, setters, locals) {
|
|
|
818
824
|
}
|
|
819
825
|
if (ts.isParenthesizedExpression(expression))
|
|
820
826
|
return valueExpr(expression.expression, setters, locals);
|
|
827
|
+
if (ts.isBinaryExpression(expression) &&
|
|
828
|
+
(expression.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || expression.operatorToken.kind === ts.SyntaxKind.BarBarToken)) {
|
|
829
|
+
return booleanExpr(expression, setters, locals);
|
|
830
|
+
}
|
|
821
831
|
if (ts.isBinaryExpression(expression) && expression.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
822
832
|
return nullishOptionalReadExpr(expression, setters, locals);
|
|
823
833
|
}
|
|
@@ -1076,6 +1086,66 @@ function callSummaryFromHandler(handler, setters, initialLocals = new Map()) {
|
|
|
1076
1086
|
}
|
|
1077
1087
|
return undefined;
|
|
1078
1088
|
}
|
|
1089
|
+
function pluginWriteTransition(source, fileName, node, attr, component, call, setters, locals, sourcePlugins, locator) {
|
|
1090
|
+
const callee = callName(call.expression);
|
|
1091
|
+
if (!callee)
|
|
1092
|
+
return undefined;
|
|
1093
|
+
const ctx = {
|
|
1094
|
+
read: (name, path) => {
|
|
1095
|
+
const local = locals.get(name);
|
|
1096
|
+
if (local?.expr.kind === "read") {
|
|
1097
|
+
return { kind: "read", var: local.expr.var, path: [...(local.expr.path ?? []), ...(path ?? [])] };
|
|
1098
|
+
}
|
|
1099
|
+
const varId = stateVarForName(name, setters) ?? name;
|
|
1100
|
+
return { kind: "read", var: varId, ...(path && path.length > 0 ? { path } : {}) };
|
|
1101
|
+
},
|
|
1102
|
+
locator
|
|
1103
|
+
};
|
|
1104
|
+
const callSite = {
|
|
1105
|
+
callee,
|
|
1106
|
+
arguments: call.arguments.map(callArgumentValue),
|
|
1107
|
+
source: { file: fileName, ...lineAndColumn(source, call) }
|
|
1108
|
+
};
|
|
1109
|
+
for (const plugin of sourcePlugins) {
|
|
1110
|
+
const summary = plugin.summarizeWrite?.(callSite, ctx);
|
|
1111
|
+
if (!summary || summary === "unsupported")
|
|
1112
|
+
continue;
|
|
1113
|
+
const reads = [...effectReads(summary)].sort();
|
|
1114
|
+
const writes = [...effectWrites(summary)].sort();
|
|
1115
|
+
return {
|
|
1116
|
+
id: `${component}.${attr}.${safeId(plugin.id)}.${safeId(callee)}`,
|
|
1117
|
+
cls: "user",
|
|
1118
|
+
label: labelForEvent(attr, locator),
|
|
1119
|
+
source: [{ file: fileName, ...lineAndColumn(source, node) }],
|
|
1120
|
+
guard: { kind: "lit", value: true },
|
|
1121
|
+
effect: summary,
|
|
1122
|
+
reads,
|
|
1123
|
+
writes,
|
|
1124
|
+
confidence: "exact"
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
return undefined;
|
|
1128
|
+
}
|
|
1129
|
+
function callArgumentValue(argument) {
|
|
1130
|
+
const literal = literalValue(argument);
|
|
1131
|
+
if (literal !== undefined)
|
|
1132
|
+
return literal;
|
|
1133
|
+
if (ts.isIdentifier(argument))
|
|
1134
|
+
return argument.text;
|
|
1135
|
+
if (ts.isObjectLiteralExpression(argument)) {
|
|
1136
|
+
const fields = {};
|
|
1137
|
+
for (const property of argument.properties) {
|
|
1138
|
+
if (!ts.isPropertyAssignment(property))
|
|
1139
|
+
return argument.getText();
|
|
1140
|
+
const name = propertyName(property.name);
|
|
1141
|
+
if (!name)
|
|
1142
|
+
return argument.getText();
|
|
1143
|
+
fields[name] = callArgumentValue(property.initializer);
|
|
1144
|
+
}
|
|
1145
|
+
return fields;
|
|
1146
|
+
}
|
|
1147
|
+
return argument.getText();
|
|
1148
|
+
}
|
|
1079
1149
|
function swrMutateTransition(source, fileName, node, attr, component, call, locator) {
|
|
1080
1150
|
if (!ts.isIdentifier(call.expression) || call.expression.text !== "mutate")
|
|
1081
1151
|
return undefined;
|
|
@@ -1099,7 +1169,8 @@ function bindConstStatement(statement, setters, locals, partialBoolean = false)
|
|
|
1099
1169
|
for (const declaration of statement.declarationList.declarations) {
|
|
1100
1170
|
if (!ts.isIdentifier(declaration.name) || !declaration.initializer)
|
|
1101
1171
|
return false;
|
|
1102
|
-
const
|
|
1172
|
+
const setterAlias = ts.isIdentifier(declaration.initializer) ? setters.get(declaration.initializer.text) ?? locals.get(declaration.initializer.text)?.setter : undefined;
|
|
1173
|
+
const binding = setterAlias ? { expr: { kind: "lit", value: null }, reads: [], setter: setterAlias } : valueExpr(declaration.initializer, setters, locals) ??
|
|
1103
1174
|
(partialBoolean ? parseConjunctiveGuardExpression(declaration.initializer, setters, locals) : booleanExpr(declaration.initializer, setters, locals));
|
|
1104
1175
|
if (!binding)
|
|
1105
1176
|
return false;
|
|
@@ -1113,8 +1184,8 @@ function inlinedHelperCall(call, handlers, setters) {
|
|
|
1113
1184
|
const helper = handlers.get(call.expression.text);
|
|
1114
1185
|
return helper ? callSummaryFromHandler(helper, setters) : undefined;
|
|
1115
1186
|
}
|
|
1116
|
-
function navigationTransition(source, fileName, node, attr, component, call, locator) {
|
|
1117
|
-
const navigation = navigationCall(call);
|
|
1187
|
+
function navigationTransition(source, fileName, node, attr, component, call, locator, routerPlugin) {
|
|
1188
|
+
const navigation = navigationCall(call, routerPlugin);
|
|
1118
1189
|
if (!navigation)
|
|
1119
1190
|
return undefined;
|
|
1120
1191
|
const routeId = navigation.to ? safeId(navigation.to) : "back";
|
|
@@ -1138,10 +1209,13 @@ function navigationTransition(source, fileName, node, attr, component, call, loc
|
|
|
1138
1209
|
confidence: "exact"
|
|
1139
1210
|
};
|
|
1140
1211
|
}
|
|
1141
|
-
function navigationCall(call) {
|
|
1212
|
+
function navigationCall(call, routerPlugin) {
|
|
1142
1213
|
const name = callName(call.expression);
|
|
1143
1214
|
if (!name)
|
|
1144
1215
|
return undefined;
|
|
1216
|
+
const pluginNavigation = routerPlugin?.navigationCall(name, call.arguments.map(callArgumentValue));
|
|
1217
|
+
if (pluginNavigation && pluginNavigation !== "unsupported")
|
|
1218
|
+
return pluginNavigation;
|
|
1145
1219
|
if (name === "navigate" && call.arguments.length === 1) {
|
|
1146
1220
|
const to = literalValue(call.arguments[0]);
|
|
1147
1221
|
return typeof to === "string" ? { mode: "push", to } : undefined;
|
|
@@ -1157,10 +1231,10 @@ function navigationCall(call) {
|
|
|
1157
1231
|
}
|
|
1158
1232
|
return undefined;
|
|
1159
1233
|
}
|
|
1160
|
-
function escapedSetters(call, setters) {
|
|
1234
|
+
function escapedSetters(call, setters, locals = new Map()) {
|
|
1161
1235
|
return call.arguments
|
|
1162
1236
|
.filter(ts.isIdentifier)
|
|
1163
|
-
.map((arg) => setters.get(arg.text))
|
|
1237
|
+
.map((arg) => setters.get(arg.text) ?? locals.get(arg.text)?.setter)
|
|
1164
1238
|
.filter((setter) => Boolean(setter));
|
|
1165
1239
|
}
|
|
1166
1240
|
function escapedSetterTransitions(source, fileName, node, attr, component, setters, locator) {
|
|
@@ -1990,6 +2064,7 @@ function renderGuardFor(eventAttribute, setters, warnings, source, component, lo
|
|
|
1990
2064
|
const element = jsxElementForAttribute(eventAttribute);
|
|
1991
2065
|
if (!element)
|
|
1992
2066
|
return undefined;
|
|
2067
|
+
const guards = [];
|
|
1993
2068
|
let current = element;
|
|
1994
2069
|
while (current.parent) {
|
|
1995
2070
|
const parent = current.parent;
|
|
@@ -2001,7 +2076,9 @@ function renderGuardFor(eventAttribute, setters, warnings, source, component, lo
|
|
|
2001
2076
|
warnings.push({ message: `Unsupported render guard ${component}.${eventAttribute.name.getText(source)}`, ...lineAndColumn(source, parent.left) });
|
|
2002
2077
|
return undefined;
|
|
2003
2078
|
}
|
|
2004
|
-
|
|
2079
|
+
guards.push(parsed);
|
|
2080
|
+
current = parent;
|
|
2081
|
+
continue;
|
|
2005
2082
|
}
|
|
2006
2083
|
if (ts.isConditionalExpression(parent) && parent.whenTrue === current) {
|
|
2007
2084
|
const parsed = parseConjunctiveGuardExpression(parent.condition, setters, locals);
|
|
@@ -2009,7 +2086,9 @@ function renderGuardFor(eventAttribute, setters, warnings, source, component, lo
|
|
|
2009
2086
|
warnings.push({ message: `Unsupported render guard ${component}.${eventAttribute.name.getText(source)}`, ...lineAndColumn(source, parent.condition) });
|
|
2010
2087
|
return undefined;
|
|
2011
2088
|
}
|
|
2012
|
-
|
|
2089
|
+
guards.push(parsed);
|
|
2090
|
+
current = parent;
|
|
2091
|
+
continue;
|
|
2013
2092
|
}
|
|
2014
2093
|
if (ts.isConditionalExpression(parent) && parent.whenFalse === current) {
|
|
2015
2094
|
const parsed = parseConjunctiveGuardExpression(parent.condition, setters, locals);
|
|
@@ -2017,15 +2096,17 @@ function renderGuardFor(eventAttribute, setters, warnings, source, component, lo
|
|
|
2017
2096
|
warnings.push({ message: `Unsupported render guard ${component}.${eventAttribute.name.getText(source)}`, ...lineAndColumn(source, parent.condition) });
|
|
2018
2097
|
return undefined;
|
|
2019
2098
|
}
|
|
2020
|
-
|
|
2099
|
+
guards.push({ expr: { kind: "not", args: [parsed.expr] }, reads: parsed.reads });
|
|
2100
|
+
current = parent;
|
|
2101
|
+
continue;
|
|
2021
2102
|
}
|
|
2022
|
-
if (ts.isParenthesizedExpression(parent) || ts.isJsxExpression(parent)) {
|
|
2103
|
+
if (ts.isParenthesizedExpression(parent) || ts.isJsxExpression(parent) || ts.isJsxElement(parent) || ts.isJsxFragment(parent)) {
|
|
2023
2104
|
current = parent;
|
|
2024
2105
|
continue;
|
|
2025
2106
|
}
|
|
2026
|
-
return
|
|
2107
|
+
return combineParsedGuards(guards);
|
|
2027
2108
|
}
|
|
2028
|
-
return
|
|
2109
|
+
return combineParsedGuards(guards);
|
|
2029
2110
|
}
|
|
2030
2111
|
function jsxElementForAttribute(attribute) {
|
|
2031
2112
|
const attrs = attribute.parent;
|