@appilots/cli 0.11.3 → 0.13.0
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 +23 -11
- package/dist/cli/index.js +1015 -390
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.mts +40 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +549 -71
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +547 -69
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -28,10 +28,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
));
|
|
29
29
|
|
|
30
30
|
// src/cli/index.ts
|
|
31
|
-
var
|
|
31
|
+
var import_commander9 = require("commander");
|
|
32
32
|
|
|
33
33
|
// src/version.ts
|
|
34
|
-
var CLI_VERSION = "0.
|
|
34
|
+
var CLI_VERSION = "0.13.0";
|
|
35
35
|
|
|
36
36
|
// src/config/index.ts
|
|
37
37
|
var import_fs = require("fs");
|
|
@@ -52,7 +52,8 @@ var KNOWN_CONFIG_KEYS = [
|
|
|
52
52
|
"navigationExclude",
|
|
53
53
|
"platform",
|
|
54
54
|
"manifestPath",
|
|
55
|
-
"eval"
|
|
55
|
+
"eval",
|
|
56
|
+
"knowledge"
|
|
56
57
|
];
|
|
57
58
|
var KEY_ALIASES = {
|
|
58
59
|
apiUrl: "serverUrl",
|
|
@@ -200,7 +201,8 @@ function saveConfig(dir, config) {
|
|
|
200
201
|
navigationExclude: config.navigationExclude || existingConfig?.navigationExclude,
|
|
201
202
|
platform: config.platform || existingConfig?.platform,
|
|
202
203
|
manifestPath: config.manifestPath || existingConfig?.manifestPath,
|
|
203
|
-
eval: config.eval || existingConfig?.eval
|
|
204
|
+
eval: config.eval || existingConfig?.eval,
|
|
205
|
+
knowledge: config.knowledge || existingConfig?.knowledge
|
|
204
206
|
};
|
|
205
207
|
try {
|
|
206
208
|
(0, import_fs.writeFileSync)(configPath, JSON.stringify(mergedConfig, null, 2), "utf-8");
|
|
@@ -293,6 +295,19 @@ function validateConfig(config) {
|
|
|
293
295
|
if (config.manifestPath !== void 0 && typeof config.manifestPath !== "string") {
|
|
294
296
|
errors.push("manifestPath must be a string");
|
|
295
297
|
}
|
|
298
|
+
if (config.knowledge !== void 0) {
|
|
299
|
+
if (typeof config.knowledge !== "object" || config.knowledge === null || Array.isArray(config.knowledge)) {
|
|
300
|
+
errors.push("knowledge must be an object");
|
|
301
|
+
} else {
|
|
302
|
+
const kc = config.knowledge;
|
|
303
|
+
for (const field of ["sources", "exclude"]) {
|
|
304
|
+
const value = kc[field];
|
|
305
|
+
if (value !== void 0 && (!Array.isArray(value) || !value.every((item) => typeof item === "string"))) {
|
|
306
|
+
errors.push(`knowledge.${field} must be an array of strings`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
296
311
|
if (config.eval !== void 0) {
|
|
297
312
|
if (typeof config.eval !== "object" || config.eval === null || Array.isArray(config.eval)) {
|
|
298
313
|
errors.push("eval must be an object");
|
|
@@ -634,6 +649,48 @@ var AppilotsAPIClient = class {
|
|
|
634
649
|
};
|
|
635
650
|
}
|
|
636
651
|
}
|
|
652
|
+
/**
|
|
653
|
+
* Syncs knowledge documents with the Appilots backend.
|
|
654
|
+
*
|
|
655
|
+
* @param documents Array of documents to sync (filename, base64 content, mimeType, checksum)
|
|
656
|
+
* @returns KnowledgeSyncResult with counts of uploaded, skipped, and errored docs
|
|
657
|
+
*/
|
|
658
|
+
async knowledgeSync(documents) {
|
|
659
|
+
try {
|
|
660
|
+
const response = await this.request(`${this.baseUrl}/cli/knowledge/sync`, {
|
|
661
|
+
method: "POST",
|
|
662
|
+
headers: {
|
|
663
|
+
"Content-Type": "application/json",
|
|
664
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
665
|
+
},
|
|
666
|
+
body: JSON.stringify({ documents })
|
|
667
|
+
});
|
|
668
|
+
if (!response.ok) {
|
|
669
|
+
const errorData = await response.json().catch(() => ({}));
|
|
670
|
+
return {
|
|
671
|
+
success: false,
|
|
672
|
+
uploaded: 0,
|
|
673
|
+
skipped: 0,
|
|
674
|
+
errors: 0,
|
|
675
|
+
error: describeApiError(errorData, `HTTP ${response.status}: ${response.statusText}`)
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
const json = await response.json();
|
|
679
|
+
const inner = json.data ?? json;
|
|
680
|
+
return {
|
|
681
|
+
success: true,
|
|
682
|
+
...inner
|
|
683
|
+
};
|
|
684
|
+
} catch (error2) {
|
|
685
|
+
return {
|
|
686
|
+
success: false,
|
|
687
|
+
uploaded: 0,
|
|
688
|
+
skipped: 0,
|
|
689
|
+
errors: 0,
|
|
690
|
+
error: error2 instanceof Error ? error2.message : "Failed to sync knowledge with Appilots API"
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
}
|
|
637
694
|
/**
|
|
638
695
|
* Checks if the Appilots API server is healthy
|
|
639
696
|
*
|
|
@@ -771,7 +828,7 @@ var import_node_path7 = require("path");
|
|
|
771
828
|
|
|
772
829
|
// src/generators/MCPGenerator.ts
|
|
773
830
|
var import_promises4 = require("fs/promises");
|
|
774
|
-
var
|
|
831
|
+
var import_node_crypto2 = require("crypto");
|
|
775
832
|
var import_node_path5 = __toESM(require("path"));
|
|
776
833
|
|
|
777
834
|
// src/generators/checksum.ts
|
|
@@ -1036,8 +1093,355 @@ async function globSorted(patterns, options) {
|
|
|
1036
1093
|
|
|
1037
1094
|
// src/analyzers/ScreenAnalyzer.ts
|
|
1038
1095
|
var import_promises = __toESM(require("fs/promises"));
|
|
1096
|
+
|
|
1097
|
+
// src/extractors/control-evidence.ts
|
|
1098
|
+
var import_node_crypto = require("crypto");
|
|
1099
|
+
var import_traverse = __toESM(require("@babel/traverse"));
|
|
1100
|
+
var t2 = __toESM(require("@babel/types"));
|
|
1101
|
+
|
|
1102
|
+
// src/extractors/control-evidence-bindings.ts
|
|
1103
|
+
var t = __toESM(require("@babel/types"));
|
|
1104
|
+
var MAX_HOPS = 8;
|
|
1105
|
+
function unwrap(path13) {
|
|
1106
|
+
while (path13.isTSAsExpression() || path13.isTSTypeAssertion() || path13.isTSNonNullExpression() || path13.isTSSatisfiesExpression() || path13.isParenthesizedExpression())
|
|
1107
|
+
path13 = path13.get("expression");
|
|
1108
|
+
return path13;
|
|
1109
|
+
}
|
|
1110
|
+
function constantValue(path13, depth = 0) {
|
|
1111
|
+
if (depth > MAX_HOPS) return void 0;
|
|
1112
|
+
path13 = unwrap(path13);
|
|
1113
|
+
if (!path13.isIdentifier()) return path13;
|
|
1114
|
+
const binding = path13.scope.getBinding(path13.node.name);
|
|
1115
|
+
if (!binding?.constant || !binding.path.isVariableDeclarator()) return path13;
|
|
1116
|
+
const init = binding.path.get("init");
|
|
1117
|
+
const resolved = init.node ? constantValue(init, depth + 1) : void 0;
|
|
1118
|
+
if (resolved?.isObjectExpression() && binding.referencePaths.some(
|
|
1119
|
+
(reference) => !reference.parentPath?.isJSXSpreadAttribute() && !reference.parentPath?.isSpreadElement()
|
|
1120
|
+
))
|
|
1121
|
+
return void 0;
|
|
1122
|
+
return resolved;
|
|
1123
|
+
}
|
|
1124
|
+
function propValue(path13, name) {
|
|
1125
|
+
const readObject = (path14) => {
|
|
1126
|
+
const resolved = constantValue(path14);
|
|
1127
|
+
if (!resolved?.isObjectExpression()) return { blocked: true };
|
|
1128
|
+
for (const property of [...resolved.get("properties")].reverse()) {
|
|
1129
|
+
if (property.isSpreadElement()) {
|
|
1130
|
+
const found = readObjectBounded(property.get("argument"));
|
|
1131
|
+
if (found.value || found.blocked) return found;
|
|
1132
|
+
} else if (property.isObjectProperty() || property.isObjectMethod()) {
|
|
1133
|
+
if (property.node.computed) return { blocked: true };
|
|
1134
|
+
const key = property.node.key;
|
|
1135
|
+
if ((t.isIdentifier(key) ? key.name : t.isStringLiteral(key) ? key.value : "") === name)
|
|
1136
|
+
return property.isObjectProperty() ? { value: property.get("value") } : { value: property };
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return {};
|
|
1140
|
+
};
|
|
1141
|
+
let objectBudget = MAX_HOPS;
|
|
1142
|
+
const readObjectBounded = (path14) => objectBudget-- > 0 ? readObject(path14) : { blocked: true };
|
|
1143
|
+
for (const attr of [...path13.get("attributes")].reverse()) {
|
|
1144
|
+
if (attr.isJSXAttribute() && t.isJSXIdentifier(attr.node.name, { name })) {
|
|
1145
|
+
const value = attr.get("value");
|
|
1146
|
+
return value.isJSXExpressionContainer() ? unwrap(value.get("expression")) : value.node ? value : void 0;
|
|
1147
|
+
}
|
|
1148
|
+
if (attr.isJSXSpreadAttribute()) {
|
|
1149
|
+
const found = readObjectBounded(attr.get("argument"));
|
|
1150
|
+
if (found.value || found.blocked) return found.value;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
return void 0;
|
|
1154
|
+
}
|
|
1155
|
+
function importIdentity(path13, depth = 0) {
|
|
1156
|
+
if (depth > MAX_HOPS) return void 0;
|
|
1157
|
+
path13 = unwrap(path13);
|
|
1158
|
+
if (path13.isIdentifier() || path13.isJSXIdentifier()) {
|
|
1159
|
+
const binding = path13.scope.getBinding(path13.node.name);
|
|
1160
|
+
if (!binding?.constant) return void 0;
|
|
1161
|
+
const declaration = binding.path;
|
|
1162
|
+
if (declaration.parentPath?.isImportDeclaration()) {
|
|
1163
|
+
const module2 = declaration.parentPath.node.source.value;
|
|
1164
|
+
if (declaration.isImportSpecifier()) {
|
|
1165
|
+
const imported = declaration.node.imported;
|
|
1166
|
+
return { module: module2, imported: t.isIdentifier(imported) ? imported.name : imported.value };
|
|
1167
|
+
}
|
|
1168
|
+
if (declaration.isImportDefaultSpecifier()) return { module: module2, imported: "default" };
|
|
1169
|
+
if (declaration.isImportNamespaceSpecifier()) return { module: module2, imported: "*" };
|
|
1170
|
+
}
|
|
1171
|
+
if (declaration.isVariableDeclarator() && declaration.get("init").node)
|
|
1172
|
+
return importIdentity(declaration.get("init"), depth + 1);
|
|
1173
|
+
}
|
|
1174
|
+
if (path13.isJSXMemberExpression() || path13.isMemberExpression() && !path13.node.computed) {
|
|
1175
|
+
const origin = importIdentity(path13.get("object"), depth + 1);
|
|
1176
|
+
const key = path13.node.property;
|
|
1177
|
+
if (origin && (t.isIdentifier(key) || t.isJSXIdentifier(key)) && (origin.imported === "*" || origin.module === "react" && origin.imported === "default"))
|
|
1178
|
+
return { module: origin.module, imported: key.name };
|
|
1179
|
+
}
|
|
1180
|
+
return void 0;
|
|
1181
|
+
}
|
|
1182
|
+
function handlerFunction(path13, depth = 0) {
|
|
1183
|
+
if (depth > MAX_HOPS) return void 0;
|
|
1184
|
+
path13 = unwrap(path13);
|
|
1185
|
+
if (path13.isFunction()) return path13;
|
|
1186
|
+
if (path13.isIdentifier()) {
|
|
1187
|
+
const binding = path13.scope.getBinding(path13.node.name);
|
|
1188
|
+
if (!binding?.constant) return void 0;
|
|
1189
|
+
if (binding.path.isFunctionDeclaration()) return binding.path;
|
|
1190
|
+
if (binding.path.isVariableDeclarator() && binding.path.get("init").node)
|
|
1191
|
+
return handlerFunction(binding.path.get("init"), depth + 1);
|
|
1192
|
+
}
|
|
1193
|
+
if (path13.isCallExpression()) {
|
|
1194
|
+
const origin = importIdentity(path13.get("callee"));
|
|
1195
|
+
if (origin?.module === "react" && origin.imported === "useCallback") {
|
|
1196
|
+
const callback = path13.get("arguments")[0];
|
|
1197
|
+
if (callback) return handlerFunction(callback, depth + 1);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
if (path13.isMemberExpression() && !path13.node.computed && t.isThisExpression(path13.node.object)) {
|
|
1201
|
+
const key = path13.node.property;
|
|
1202
|
+
if (!t.isIdentifier(key)) return void 0;
|
|
1203
|
+
const owner = path13.findParent((p) => p.isClassDeclaration() || p.isClassExpression());
|
|
1204
|
+
if (!owner || !(owner.isClassDeclaration() || owner.isClassExpression())) return void 0;
|
|
1205
|
+
for (const member of owner.get("body").get("body")) {
|
|
1206
|
+
if (!(member.isClassMethod() || member.isClassProperty()) || member.node.computed || member.node.static)
|
|
1207
|
+
continue;
|
|
1208
|
+
if (!t.isIdentifier(member.node.key, { name: key.name })) continue;
|
|
1209
|
+
if (member.isClassMethod() && member.node.kind === "method") return member;
|
|
1210
|
+
if (member.isClassProperty() && member.get("value").node)
|
|
1211
|
+
return handlerFunction(member.get("value"), depth + 1);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
return void 0;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// src/extractors/control-evidence.ts
|
|
1218
|
+
function symbol(node) {
|
|
1219
|
+
if (t2.isTSAsExpression(node) || t2.isTSTypeAssertion(node) || t2.isTSNonNullExpression(node) || t2.isTSSatisfiesExpression(node))
|
|
1220
|
+
return symbol(node.expression);
|
|
1221
|
+
if (t2.isIdentifier(node) || t2.isJSXIdentifier(node)) return node.name;
|
|
1222
|
+
if (t2.isThisExpression(node)) return "this";
|
|
1223
|
+
if (t2.isMemberExpression(node) && !node.computed || t2.isJSXMemberExpression(node)) {
|
|
1224
|
+
const object = symbol(node.object), property = symbol(node.property);
|
|
1225
|
+
return object && property ? `${object}.${property}` : void 0;
|
|
1226
|
+
}
|
|
1227
|
+
if (t2.isUnaryExpression(node) && node.operator === "!") {
|
|
1228
|
+
const value = symbol(node.argument);
|
|
1229
|
+
return value ? `!${value}` : void 0;
|
|
1230
|
+
}
|
|
1231
|
+
return void 0;
|
|
1232
|
+
}
|
|
1233
|
+
function attribute(node, name) {
|
|
1234
|
+
return node.attributes.find(
|
|
1235
|
+
(a) => t2.isJSXAttribute(a) && t2.isJSXIdentifier(a.name, { name })
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
var ICON_LIBRARIES = [
|
|
1239
|
+
"lucide-react-native",
|
|
1240
|
+
"lucide-react",
|
|
1241
|
+
"@tamagui/lucide-icons",
|
|
1242
|
+
"@expo/vector-icons",
|
|
1243
|
+
"@react-native-vector-icons/",
|
|
1244
|
+
"react-native-vector-icons/"
|
|
1245
|
+
];
|
|
1246
|
+
function iconLibrary(module2) {
|
|
1247
|
+
return ICON_LIBRARIES.some(
|
|
1248
|
+
(name) => name.endsWith("/") ? module2.startsWith(name) : module2 === name || module2.startsWith(name + "/")
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
function add(values, value, max = 12) {
|
|
1252
|
+
if (value && value.length <= 240 && values.length < max && !values.includes(value))
|
|
1253
|
+
values.push(value);
|
|
1254
|
+
}
|
|
1255
|
+
function iconName(path13, explicitIcon = false) {
|
|
1256
|
+
const origin = importIdentity(path13);
|
|
1257
|
+
if (origin && iconLibrary(origin.module) && origin.imported !== "*")
|
|
1258
|
+
return origin.imported === "default" ? origin.module : `${origin.module}:${origin.imported}`;
|
|
1259
|
+
const name = symbol(path13.node);
|
|
1260
|
+
return name && (explicitIcon || /icon/i.test(name)) ? name : void 0;
|
|
1261
|
+
}
|
|
1262
|
+
function iconsInElement(path13, explicitIcon = false) {
|
|
1263
|
+
const name = iconName(path13.get("name"), explicitIcon);
|
|
1264
|
+
if (!name) return void 0;
|
|
1265
|
+
const glyph = propValue(path13, "name");
|
|
1266
|
+
const value = glyph && constantValue(glyph);
|
|
1267
|
+
return value?.isStringLiteral() ? `${name}:${value.node.value}` : name;
|
|
1268
|
+
}
|
|
1269
|
+
function hasInteraction(path13) {
|
|
1270
|
+
return ["onPress", "onLongPress", "onClick"].some(
|
|
1271
|
+
(name) => attribute(path13.node, name) || propValue(path13, name)
|
|
1272
|
+
);
|
|
1273
|
+
}
|
|
1274
|
+
function collectPresentationIcons(path13, icons) {
|
|
1275
|
+
const namedIconProps = [
|
|
1276
|
+
"icon",
|
|
1277
|
+
"prefix",
|
|
1278
|
+
"suffix",
|
|
1279
|
+
"left",
|
|
1280
|
+
"right",
|
|
1281
|
+
"leadingIcon",
|
|
1282
|
+
"trailingIcon",
|
|
1283
|
+
"startIcon",
|
|
1284
|
+
"endIcon",
|
|
1285
|
+
"renderIcon"
|
|
1286
|
+
];
|
|
1287
|
+
const props = new Set(namedIconProps);
|
|
1288
|
+
for (const attr of path13.node.attributes)
|
|
1289
|
+
if (t2.isJSXAttribute(attr) && t2.isJSXIdentifier(attr.name) && !/^on[A-Z]/.test(attr.name.name))
|
|
1290
|
+
props.add(attr.name.name);
|
|
1291
|
+
for (const prop of props) {
|
|
1292
|
+
const icon = propValue(path13, prop);
|
|
1293
|
+
if (icon) {
|
|
1294
|
+
const value = constantValue(icon);
|
|
1295
|
+
if (value?.isStringLiteral()) {
|
|
1296
|
+
if (prop.toLowerCase().includes("icon")) add(icons, value.node.value, 8);
|
|
1297
|
+
} else {
|
|
1298
|
+
const explicitIcon = prop.toLowerCase() === "icon";
|
|
1299
|
+
if (icon.isJSXElement()) {
|
|
1300
|
+
if (hasInteraction(icon.get("openingElement"))) continue;
|
|
1301
|
+
add(icons, iconsInElement(icon.get("openingElement"), explicitIcon), 8);
|
|
1302
|
+
}
|
|
1303
|
+
icon.traverse({
|
|
1304
|
+
JSXAttribute(attr) {
|
|
1305
|
+
if (t2.isJSXIdentifier(attr.node.name) && /^on[A-Z]/.test(attr.node.name.name))
|
|
1306
|
+
attr.skip();
|
|
1307
|
+
},
|
|
1308
|
+
JSXElement(child) {
|
|
1309
|
+
const opening = child.get("openingElement");
|
|
1310
|
+
if (hasInteraction(opening)) {
|
|
1311
|
+
child.skip();
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
add(icons, iconsInElement(opening, explicitIcon), 8);
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
if (!icons.length && (namedIconProps.includes(prop) || explicitIcon))
|
|
1318
|
+
add(icons, iconName(icon, explicitIcon), 8);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
function collectIcons(path13) {
|
|
1324
|
+
const icons = [];
|
|
1325
|
+
collectPresentationIcons(path13, icons);
|
|
1326
|
+
const jsx = path13.parentPath;
|
|
1327
|
+
if (jsx.isJSXElement())
|
|
1328
|
+
jsx.traverse({
|
|
1329
|
+
// JSX mentioned inside an event callback is not a rendered child icon.
|
|
1330
|
+
JSXAttribute(attr) {
|
|
1331
|
+
attr.skip();
|
|
1332
|
+
},
|
|
1333
|
+
JSXElement(child) {
|
|
1334
|
+
const opening = child.get("openingElement");
|
|
1335
|
+
if (hasInteraction(opening)) {
|
|
1336
|
+
child.skip();
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
add(icons, iconsInElement(opening), 8);
|
|
1340
|
+
collectPresentationIcons(opening, icons);
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
return icons;
|
|
1344
|
+
}
|
|
1345
|
+
function conditionsAt(path13) {
|
|
1346
|
+
const conditions = [];
|
|
1347
|
+
let child = path13;
|
|
1348
|
+
for (let parent = child.parentPath; parent && !parent.isFunction(); child = parent, parent = parent.parentPath) {
|
|
1349
|
+
let test;
|
|
1350
|
+
let negated = false;
|
|
1351
|
+
if (parent.isLogicalExpression() && parent.node.right === child.node) {
|
|
1352
|
+
if (parent.node.operator === "&&") test = parent.node.left;
|
|
1353
|
+
if (parent.node.operator === "||") {
|
|
1354
|
+
test = parent.node.left;
|
|
1355
|
+
negated = true;
|
|
1356
|
+
}
|
|
1357
|
+
} else if (parent.isConditionalExpression() && parent.node.test !== child.node) {
|
|
1358
|
+
test = parent.node.test;
|
|
1359
|
+
negated = parent.node.alternate === child.node;
|
|
1360
|
+
} else if (parent.isIfStatement() && parent.node.test !== child.node) {
|
|
1361
|
+
test = parent.node.test;
|
|
1362
|
+
negated = parent.node.alternate === child.node;
|
|
1363
|
+
}
|
|
1364
|
+
const name = symbol(test);
|
|
1365
|
+
if (name) add(conditions, negated ? name.startsWith("!") ? name.slice(1) : `!${name}` : name);
|
|
1366
|
+
}
|
|
1367
|
+
return conditions;
|
|
1368
|
+
}
|
|
1369
|
+
function collectHandlerEvidence(expression, evidence) {
|
|
1370
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1371
|
+
let budget = 100;
|
|
1372
|
+
const visit = (path13, depth) => {
|
|
1373
|
+
if (depth > 4 || budget-- <= 0) return;
|
|
1374
|
+
const fn = handlerFunction(path13);
|
|
1375
|
+
if (!fn || seen.has(fn.node)) return;
|
|
1376
|
+
seen.add(fn.node);
|
|
1377
|
+
fn.traverse({
|
|
1378
|
+
// Ignore uncalled helper definitions; inline callbacks remain source evidence.
|
|
1379
|
+
Function(nested) {
|
|
1380
|
+
if (!nested.parentPath.isCallExpression() && !nested.parentPath.isObjectProperty())
|
|
1381
|
+
nested.skip();
|
|
1382
|
+
},
|
|
1383
|
+
CallExpression(call) {
|
|
1384
|
+
if (budget-- <= 0) {
|
|
1385
|
+
call.skip();
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
add(evidence.calls, symbol(call.node.callee));
|
|
1389
|
+
for (const arg of call.node.arguments) add(evidence.argumentBindings, symbol(arg));
|
|
1390
|
+
visit(call.get("callee"), depth + 1);
|
|
1391
|
+
if (!t2.isMemberExpression(call.node.callee) || call.node.callee.computed || !t2.isIdentifier(call.node.callee.property, { name: "alert" }))
|
|
1392
|
+
return;
|
|
1393
|
+
const callee = call.get("callee");
|
|
1394
|
+
if (!callee.isMemberExpression()) return;
|
|
1395
|
+
const origin = importIdentity(callee.get("object"));
|
|
1396
|
+
if (origin?.module !== "react-native" || origin.imported !== "Alert") return;
|
|
1397
|
+
const options = call.node.arguments[2];
|
|
1398
|
+
const destructiveOption = t2.isArrayExpression(options) && options.elements.some(
|
|
1399
|
+
(option) => t2.isObjectExpression(option) && option.properties.some(
|
|
1400
|
+
(p) => t2.isObjectProperty(p) && !p.computed && symbol(p.key) === "style" && t2.isStringLiteral(p.value, { value: "destructive" })
|
|
1401
|
+
)
|
|
1402
|
+
);
|
|
1403
|
+
if (destructiveOption)
|
|
1404
|
+
evidence.nativeConfirmation = {
|
|
1405
|
+
title: symbol(call.node.arguments[0]),
|
|
1406
|
+
destructiveOption
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
});
|
|
1410
|
+
};
|
|
1411
|
+
visit(unwrap(expression), 0);
|
|
1412
|
+
}
|
|
1413
|
+
function extractControlEvidence(ast, source, file2) {
|
|
1414
|
+
const candidates = [];
|
|
1415
|
+
const sourceHash = (0, import_node_crypto.createHash)("sha256").update(source).digest("hex");
|
|
1416
|
+
(0, import_traverse.default)(ast, {
|
|
1417
|
+
JSXOpeningElement(path13) {
|
|
1418
|
+
const node = path13.node;
|
|
1419
|
+
if (attribute(node, "__appilotsControl")) return;
|
|
1420
|
+
const value = propValue(path13, "onPress");
|
|
1421
|
+
if (!value && !attribute(node, "onPress") || value?.isNullLiteral() || value?.isJSXEmptyExpression())
|
|
1422
|
+
return;
|
|
1423
|
+
const icons = collectIcons(path13);
|
|
1424
|
+
if (!icons.length || node.end == null) return;
|
|
1425
|
+
const evidence = {
|
|
1426
|
+
version: 1,
|
|
1427
|
+
siteId: (0, import_node_crypto.createHash)("sha256").update(`${file2}:${sourceHash}:${node.start}`).digest("hex").slice(0, 20),
|
|
1428
|
+
component: symbol(node.name) ?? "unknown",
|
|
1429
|
+
icons,
|
|
1430
|
+
...value && symbol(value.node) ? { handler: symbol(value.node) } : {},
|
|
1431
|
+
calls: [],
|
|
1432
|
+
argumentBindings: [],
|
|
1433
|
+
conditions: conditionsAt(path13)
|
|
1434
|
+
};
|
|
1435
|
+
if (value) collectHandlerEvidence(value, evidence);
|
|
1436
|
+
candidates.push({ evidence, sourceHash, offset: node.end - (node.selfClosing ? 2 : 1) });
|
|
1437
|
+
}
|
|
1438
|
+
});
|
|
1439
|
+
return candidates;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
// src/analyzers/ScreenAnalyzer.ts
|
|
1039
1443
|
var import_path3 = __toESM(require("path"));
|
|
1040
|
-
var
|
|
1444
|
+
var import_traverse5 = __toESM(require("@babel/traverse"));
|
|
1041
1445
|
var BabelTypes = __toESM(require("@babel/types"));
|
|
1042
1446
|
|
|
1043
1447
|
// src/ast/parse.ts
|
|
@@ -1062,25 +1466,25 @@ function parseSource(source, parserPlugins = []) {
|
|
|
1062
1466
|
}
|
|
1063
1467
|
|
|
1064
1468
|
// src/ast/jsx/attributes.ts
|
|
1065
|
-
var
|
|
1469
|
+
var t3 = __toESM(require("@babel/types"));
|
|
1066
1470
|
function findJsxAttribute(element, name) {
|
|
1067
1471
|
return element.attributes.find(
|
|
1068
|
-
(attr) =>
|
|
1472
|
+
(attr) => t3.isJSXAttribute(attr) && t3.isJSXIdentifier(attr.name) && attr.name.name === name
|
|
1069
1473
|
);
|
|
1070
1474
|
}
|
|
1071
1475
|
function getStringAttr(element, name) {
|
|
1072
1476
|
const attr = findJsxAttribute(element, name);
|
|
1073
1477
|
if (!attr || !attr.value) return void 0;
|
|
1074
|
-
if (
|
|
1075
|
-
if (
|
|
1478
|
+
if (t3.isStringLiteral(attr.value)) return attr.value.value;
|
|
1479
|
+
if (t3.isJSXExpressionContainer(attr.value) && t3.isStringLiteral(attr.value.expression)) {
|
|
1076
1480
|
return attr.value.expression.value;
|
|
1077
1481
|
}
|
|
1078
1482
|
return void 0;
|
|
1079
1483
|
}
|
|
1080
1484
|
function getExpressionIdentifierAttr(element, name) {
|
|
1081
1485
|
const attr = findJsxAttribute(element, name);
|
|
1082
|
-
if (!attr?.value || !
|
|
1083
|
-
return
|
|
1486
|
+
if (!attr?.value || !t3.isJSXExpressionContainer(attr.value)) return void 0;
|
|
1487
|
+
return t3.isIdentifier(attr.value.expression) ? attr.value.expression.name : void 0;
|
|
1084
1488
|
}
|
|
1085
1489
|
function hasJsxAttribute(element, name) {
|
|
1086
1490
|
return Boolean(findJsxAttribute(element, name));
|
|
@@ -1159,18 +1563,18 @@ function classifyJsxComponent(name, element) {
|
|
|
1159
1563
|
}
|
|
1160
1564
|
|
|
1161
1565
|
// src/ast/functions/collect.ts
|
|
1162
|
-
var
|
|
1163
|
-
var
|
|
1566
|
+
var import_traverse2 = __toESM(require("@babel/traverse"));
|
|
1567
|
+
var t4 = __toESM(require("@babel/types"));
|
|
1164
1568
|
function collectFunctions(ast) {
|
|
1165
1569
|
const handlers = /* @__PURE__ */ new Map();
|
|
1166
|
-
(0,
|
|
1570
|
+
(0, import_traverse2.default)(ast, {
|
|
1167
1571
|
FunctionDeclaration: (nodePath) => {
|
|
1168
1572
|
if (nodePath.node.id?.name) handlers.set(nodePath.node.id.name, nodePath.node);
|
|
1169
1573
|
},
|
|
1170
1574
|
VariableDeclarator: (nodePath) => {
|
|
1171
|
-
if (!
|
|
1575
|
+
if (!t4.isIdentifier(nodePath.node.id)) return;
|
|
1172
1576
|
const init = nodePath.node.init;
|
|
1173
|
-
if (
|
|
1577
|
+
if (t4.isArrowFunctionExpression(init) || t4.isFunctionExpression(init)) {
|
|
1174
1578
|
handlers.set(nodePath.node.id.name, init);
|
|
1175
1579
|
}
|
|
1176
1580
|
}
|
|
@@ -1179,8 +1583,8 @@ function collectFunctions(ast) {
|
|
|
1179
1583
|
}
|
|
1180
1584
|
|
|
1181
1585
|
// src/ast/functions/analyze-async.ts
|
|
1182
|
-
var
|
|
1183
|
-
var
|
|
1586
|
+
var import_traverse3 = __toESM(require("@babel/traverse"));
|
|
1587
|
+
var t5 = __toESM(require("@babel/types"));
|
|
1184
1588
|
var DESTRUCTIVE_VERB = /(delete|destroy|remove|discard|wipe|erase|drop|terminate|revoke|deactivate|disable)/i;
|
|
1185
1589
|
function analyzeFunctionBehavior(name, fn, handlers, seen = /* @__PURE__ */ new Set()) {
|
|
1186
1590
|
if (seen.has(name)) {
|
|
@@ -1202,41 +1606,41 @@ function analyzeFunctionBehavior(name, fn, handlers, seen = /* @__PURE__ */ new
|
|
|
1202
1606
|
let destructive = DESTRUCTIVE_VERB.test(name);
|
|
1203
1607
|
const loadingStateBindings = /* @__PURE__ */ new Set();
|
|
1204
1608
|
const inspectCall = (node) => {
|
|
1205
|
-
if (
|
|
1609
|
+
if (t5.isMemberExpression(node.callee) && t5.isIdentifier(node.callee.property)) {
|
|
1206
1610
|
const method = node.callee.property.name;
|
|
1207
1611
|
if (method === "then" || method === "catch" || method === "finally") hasThen = true;
|
|
1208
|
-
if (
|
|
1612
|
+
if (t5.isIdentifier(node.callee.object) && node.callee.object.name === "navigation" && ["navigate", "push", "replace"].includes(method)) {
|
|
1209
1613
|
const firstArg = node.arguments[0];
|
|
1210
|
-
if (
|
|
1614
|
+
if (t5.isStringLiteral(firstArg)) {
|
|
1211
1615
|
targetScreen = firstArg.value;
|
|
1212
1616
|
successSignal = { type: "navigation", target: firstArg.value };
|
|
1213
1617
|
}
|
|
1214
1618
|
}
|
|
1215
|
-
if (
|
|
1619
|
+
if (t5.isIdentifier(node.callee.object) && node.callee.object.name === "navigation" && method === "goBack") {
|
|
1216
1620
|
successSignal = { type: "goBack", description: "Action returns to the previous screen" };
|
|
1217
1621
|
}
|
|
1218
|
-
if (
|
|
1622
|
+
if (t5.isIdentifier(node.callee.object) && node.callee.object.name === "Alert" && method === "alert") {
|
|
1219
1623
|
nativeConfirmationExpected = true;
|
|
1220
1624
|
}
|
|
1221
|
-
if (
|
|
1625
|
+
if (t5.isIdentifier(node.callee.object) && /(toast|notification|snackbar)/i.test(node.callee.object.name)) {
|
|
1222
1626
|
const firstArg = node.arguments[0];
|
|
1223
1627
|
const secondArg = node.arguments[1];
|
|
1224
|
-
const text =
|
|
1628
|
+
const text = t5.isStringLiteral(secondArg) ? secondArg.value : t5.isStringLiteral(firstArg) ? firstArg.value : void 0;
|
|
1225
1629
|
const signal = { type: "toast", description: text };
|
|
1226
|
-
if (/(error|danger|fail)/i.test(method) ||
|
|
1630
|
+
if (/(error|danger|fail)/i.test(method) || t5.isStringLiteral(firstArg) && /error/i.test(firstArg.value)) {
|
|
1227
1631
|
failureSignal = signal;
|
|
1228
1632
|
} else {
|
|
1229
1633
|
successSignal = successSignal ?? signal;
|
|
1230
1634
|
}
|
|
1231
1635
|
}
|
|
1232
1636
|
}
|
|
1233
|
-
if (
|
|
1637
|
+
if (t5.isIdentifier(node.callee)) {
|
|
1234
1638
|
const calleeName = node.callee.name;
|
|
1235
1639
|
if (/^set[A-Z]/.test(calleeName)) {
|
|
1236
1640
|
hasStateSetter = true;
|
|
1237
1641
|
const stateName = setterToStateName(calleeName);
|
|
1238
1642
|
const firstArg = node.arguments[0];
|
|
1239
|
-
if (stateName &&
|
|
1643
|
+
if (stateName && t5.isBooleanLiteral(firstArg)) {
|
|
1240
1644
|
if (/loading|submitting|saving|fetching|refreshing/i.test(stateName)) {
|
|
1241
1645
|
loadingStateBindings.add(stateName);
|
|
1242
1646
|
}
|
|
@@ -1263,14 +1667,14 @@ function analyzeFunctionBehavior(name, fn, handlers, seen = /* @__PURE__ */ new
|
|
|
1263
1667
|
}
|
|
1264
1668
|
};
|
|
1265
1669
|
const inspectNode = (node) => {
|
|
1266
|
-
if (
|
|
1267
|
-
if (
|
|
1268
|
-
if (
|
|
1670
|
+
if (t5.isAwaitExpression(node)) hasAwait = true;
|
|
1671
|
+
if (t5.isCallExpression(node)) inspectCall(node);
|
|
1672
|
+
if (t5.isMemberExpression(node) && t5.isIdentifier(node.property) && DESTRUCTIVE_VERB.test(node.property.name)) {
|
|
1269
1673
|
destructive = true;
|
|
1270
1674
|
}
|
|
1271
1675
|
};
|
|
1272
1676
|
if (fn.body) {
|
|
1273
|
-
(0,
|
|
1677
|
+
(0, import_traverse3.default)(fn.body, {
|
|
1274
1678
|
noScope: true,
|
|
1275
1679
|
enter: (nodePath) => inspectNode(nodePath.node)
|
|
1276
1680
|
});
|
|
@@ -1298,21 +1702,21 @@ function setterToStateName(setterName) {
|
|
|
1298
1702
|
}
|
|
1299
1703
|
|
|
1300
1704
|
// src/ast/navigation/calls.ts
|
|
1301
|
-
var
|
|
1302
|
-
var
|
|
1705
|
+
var import_traverse4 = __toESM(require("@babel/traverse"));
|
|
1706
|
+
var t6 = __toESM(require("@babel/types"));
|
|
1303
1707
|
function extractNavigationCalls(ast) {
|
|
1304
1708
|
const calls = [];
|
|
1305
|
-
(0,
|
|
1306
|
-
noScope: !
|
|
1709
|
+
(0, import_traverse4.default)(ast, {
|
|
1710
|
+
noScope: !t6.isFile(ast),
|
|
1307
1711
|
CallExpression: (nodePath) => {
|
|
1308
1712
|
const node = nodePath.node;
|
|
1309
|
-
if (!
|
|
1713
|
+
if (!t6.isMemberExpression(node.callee) || !t6.isIdentifier(node.callee.object) || node.callee.object.name !== "navigation" || !t6.isIdentifier(node.callee.property)) {
|
|
1310
1714
|
return;
|
|
1311
1715
|
}
|
|
1312
1716
|
const method = node.callee.property.name;
|
|
1313
1717
|
if (!["navigate", "push", "replace", "goBack"].includes(method)) return;
|
|
1314
1718
|
const firstArg = node.arguments[0];
|
|
1315
|
-
const targetScreen =
|
|
1719
|
+
const targetScreen = t6.isStringLiteral(firstArg) ? firstArg.value : void 0;
|
|
1316
1720
|
calls.push({
|
|
1317
1721
|
method,
|
|
1318
1722
|
...targetScreen ? { targetScreen } : {},
|
|
@@ -1324,16 +1728,16 @@ function extractNavigationCalls(ast) {
|
|
|
1324
1728
|
}
|
|
1325
1729
|
function extractNavigationParams(arg) {
|
|
1326
1730
|
const params = {};
|
|
1327
|
-
if (!arg || !
|
|
1731
|
+
if (!arg || !t6.isObjectExpression(arg)) return params;
|
|
1328
1732
|
for (const prop of arg.properties) {
|
|
1329
|
-
if (!
|
|
1330
|
-
const key =
|
|
1733
|
+
if (!t6.isObjectProperty(prop)) continue;
|
|
1734
|
+
const key = t6.isIdentifier(prop.key) ? prop.key.name : t6.isStringLiteral(prop.key) ? prop.key.value : void 0;
|
|
1331
1735
|
if (!key) continue;
|
|
1332
|
-
if (
|
|
1736
|
+
if (t6.isMemberExpression(prop.value) && t6.isIdentifier(prop.value.object) && t6.isIdentifier(prop.value.property)) {
|
|
1333
1737
|
params[key] = `${prop.value.object.name}.${prop.value.property.name}`;
|
|
1334
|
-
} else if (
|
|
1738
|
+
} else if (t6.isIdentifier(prop.value)) {
|
|
1335
1739
|
params[key] = prop.value.name;
|
|
1336
|
-
} else if (
|
|
1740
|
+
} else if (t6.isStringLiteral(prop.value)) {
|
|
1337
1741
|
params[key] = prop.value.value;
|
|
1338
1742
|
}
|
|
1339
1743
|
}
|
|
@@ -1453,6 +1857,7 @@ var ScreenAnalyzer = class {
|
|
|
1453
1857
|
routeTargetFiles;
|
|
1454
1858
|
/** §D: Count of screens filtered out in strict mode (available after analyze()) */
|
|
1455
1859
|
screensFilteredOut = 0;
|
|
1860
|
+
controlEvidenceFiles = {};
|
|
1456
1861
|
constructor(config, options) {
|
|
1457
1862
|
this.config = config;
|
|
1458
1863
|
this.routeTargetFiles = options?.routeTargetFiles ?? /* @__PURE__ */ new Set();
|
|
@@ -1553,6 +1958,11 @@ var ScreenAnalyzer = class {
|
|
|
1553
1958
|
title: registerScreenMeta?.title,
|
|
1554
1959
|
description: registerScreenMeta?.description,
|
|
1555
1960
|
components,
|
|
1961
|
+
controlCandidates: extractControlEvidence(
|
|
1962
|
+
ast,
|
|
1963
|
+
source,
|
|
1964
|
+
import_path3.default.relative(this.config.rootDir, filePath)
|
|
1965
|
+
),
|
|
1556
1966
|
forms,
|
|
1557
1967
|
actions,
|
|
1558
1968
|
navigationTargets,
|
|
@@ -1563,6 +1973,8 @@ var ScreenAnalyzer = class {
|
|
|
1563
1973
|
...permissionsFromJsDoc.isPii ? { isPii: true } : {}
|
|
1564
1974
|
} : {}
|
|
1565
1975
|
};
|
|
1976
|
+
if (descriptor.controlCandidates?.length)
|
|
1977
|
+
this.controlEvidenceFiles[import_path3.default.relative(this.config.rootDir, filePath).split(import_path3.default.sep).join("/")] = descriptor.controlCandidates;
|
|
1566
1978
|
descriptor.__hasRegisterScreen = hasRegisterScreenCall;
|
|
1567
1979
|
return descriptor;
|
|
1568
1980
|
}
|
|
@@ -1572,7 +1984,7 @@ var ScreenAnalyzer = class {
|
|
|
1572
1984
|
*/
|
|
1573
1985
|
detectRegisterScreenCall(ast) {
|
|
1574
1986
|
let found = false;
|
|
1575
|
-
(0,
|
|
1987
|
+
(0, import_traverse5.default)(ast, {
|
|
1576
1988
|
CallExpression: (nodePath) => {
|
|
1577
1989
|
if (found) return;
|
|
1578
1990
|
const callee = nodePath.node.callee;
|
|
@@ -1589,7 +2001,7 @@ var ScreenAnalyzer = class {
|
|
|
1589
2001
|
*/
|
|
1590
2002
|
extractRegisterScreenMetadata(ast) {
|
|
1591
2003
|
let metadata = null;
|
|
1592
|
-
(0,
|
|
2004
|
+
(0, import_traverse5.default)(ast, {
|
|
1593
2005
|
CallExpression: (nodePath) => {
|
|
1594
2006
|
const callee = nodePath.node.callee;
|
|
1595
2007
|
if (BabelTypes.isIdentifier(callee) && callee.name === "registerScreen" || BabelTypes.isMemberExpression(callee) && BabelTypes.isIdentifier(callee.property) && callee.property.name === "registerScreen") {
|
|
@@ -1886,7 +2298,7 @@ var ScreenAnalyzer = class {
|
|
|
1886
2298
|
*/
|
|
1887
2299
|
extractDefaultComponentName(ast) {
|
|
1888
2300
|
let componentName = "";
|
|
1889
|
-
(0,
|
|
2301
|
+
(0, import_traverse5.default)(ast, {
|
|
1890
2302
|
ExportDefaultDeclaration: (nodePath) => {
|
|
1891
2303
|
const declaration = nodePath.node.declaration;
|
|
1892
2304
|
if (BabelTypes.isFunctionDeclaration(declaration) && declaration.id?.name) {
|
|
@@ -1911,7 +2323,7 @@ var ScreenAnalyzer = class {
|
|
|
1911
2323
|
*/
|
|
1912
2324
|
extractNavigationTargets(ast) {
|
|
1913
2325
|
const targets = /* @__PURE__ */ new Set();
|
|
1914
|
-
(0,
|
|
2326
|
+
(0, import_traverse5.default)(ast, {
|
|
1915
2327
|
CallExpression: (nodePath) => {
|
|
1916
2328
|
const callee = nodePath.node.callee;
|
|
1917
2329
|
if (BabelTypes.isMemberExpression(callee) && BabelTypes.isIdentifier(callee.object) && callee.object.name === "navigation" && BabelTypes.isIdentifier(callee.property) && callee.property.name === "navigate") {
|
|
@@ -1930,7 +2342,7 @@ var ScreenAnalyzer = class {
|
|
|
1930
2342
|
extractForms(ast) {
|
|
1931
2343
|
const forms = [];
|
|
1932
2344
|
const fields = /* @__PURE__ */ new Map();
|
|
1933
|
-
(0,
|
|
2345
|
+
(0, import_traverse5.default)(ast, {
|
|
1934
2346
|
JSXOpeningElement: (nodePath) => {
|
|
1935
2347
|
const element = nodePath.node;
|
|
1936
2348
|
if (BabelTypes.isJSXIdentifier(element.name)) {
|
|
@@ -2051,7 +2463,7 @@ var ScreenAnalyzer = class {
|
|
|
2051
2463
|
extractComponents(ast) {
|
|
2052
2464
|
const components = [];
|
|
2053
2465
|
const seen = /* @__PURE__ */ new Set();
|
|
2054
|
-
(0,
|
|
2466
|
+
(0, import_traverse5.default)(ast, {
|
|
2055
2467
|
JSXOpeningElement: (nodePath) => {
|
|
2056
2468
|
const element = nodePath.node;
|
|
2057
2469
|
if (BabelTypes.isJSXIdentifier(element.name)) {
|
|
@@ -2115,7 +2527,7 @@ var ScreenAnalyzer = class {
|
|
|
2115
2527
|
const actionLabels = new Map(
|
|
2116
2528
|
actions.filter((a) => a.label).map((a) => [this.normalizeLabel(a.label), a])
|
|
2117
2529
|
);
|
|
2118
|
-
(0,
|
|
2530
|
+
(0, import_traverse5.default)(ast, {
|
|
2119
2531
|
JSXOpeningElement: (nodePath) => {
|
|
2120
2532
|
const element = nodePath.node;
|
|
2121
2533
|
if (BabelTypes.isJSXIdentifier(element.name)) {
|
|
@@ -2184,7 +2596,7 @@ var ScreenAnalyzer = class {
|
|
|
2184
2596
|
}
|
|
2185
2597
|
collectButtonHandlersByLabel(ast) {
|
|
2186
2598
|
const out = /* @__PURE__ */ new Map();
|
|
2187
|
-
(0,
|
|
2599
|
+
(0, import_traverse5.default)(ast, {
|
|
2188
2600
|
JSXOpeningElement: (nodePath) => {
|
|
2189
2601
|
const element = nodePath.node;
|
|
2190
2602
|
if (!BabelTypes.isJSXIdentifier(element.name)) return;
|
|
@@ -2266,7 +2678,7 @@ var ScreenAnalyzer = class {
|
|
|
2266
2678
|
}
|
|
2267
2679
|
};
|
|
2268
2680
|
if (fn.body) {
|
|
2269
|
-
(0,
|
|
2681
|
+
(0, import_traverse5.default)(
|
|
2270
2682
|
fn.body,
|
|
2271
2683
|
{
|
|
2272
2684
|
noScope: true,
|
|
@@ -2463,7 +2875,7 @@ var ScreenAnalyzer = class {
|
|
|
2463
2875
|
extractCollections(ast) {
|
|
2464
2876
|
const renderItemFns = this.collectRenderItemFunctions(ast);
|
|
2465
2877
|
const collections = [];
|
|
2466
|
-
(0,
|
|
2878
|
+
(0, import_traverse5.default)(ast, {
|
|
2467
2879
|
JSXOpeningElement: (nodePath) => {
|
|
2468
2880
|
const element = nodePath.node;
|
|
2469
2881
|
if (!BabelTypes.isJSXIdentifier(element.name)) return;
|
|
@@ -2500,7 +2912,7 @@ var ScreenAnalyzer = class {
|
|
|
2500
2912
|
}
|
|
2501
2913
|
collectRenderItemFunctions(ast) {
|
|
2502
2914
|
const out = /* @__PURE__ */ new Map();
|
|
2503
|
-
(0,
|
|
2915
|
+
(0, import_traverse5.default)(ast, {
|
|
2504
2916
|
VariableDeclarator: (nodePath) => {
|
|
2505
2917
|
if (!BabelTypes.isIdentifier(nodePath.node.id)) return;
|
|
2506
2918
|
const init = nodePath.node.init;
|
|
@@ -2543,7 +2955,7 @@ var ScreenAnalyzer = class {
|
|
|
2543
2955
|
}
|
|
2544
2956
|
extractRowAction(fn) {
|
|
2545
2957
|
let action;
|
|
2546
|
-
(0,
|
|
2958
|
+
(0, import_traverse5.default)(
|
|
2547
2959
|
fn.body,
|
|
2548
2960
|
{
|
|
2549
2961
|
noScope: true,
|
|
@@ -2596,7 +3008,7 @@ var ScreenAnalyzer = class {
|
|
|
2596
3008
|
} else if (BabelTypes.isIdentifier(firstParam)) {
|
|
2597
3009
|
itemNames.add(firstParam.name);
|
|
2598
3010
|
}
|
|
2599
|
-
(0,
|
|
3011
|
+
(0, import_traverse5.default)(
|
|
2600
3012
|
fn.body,
|
|
2601
3013
|
{
|
|
2602
3014
|
noScope: true,
|
|
@@ -2638,7 +3050,7 @@ var ScreenAnalyzer = class {
|
|
|
2638
3050
|
inferSearchField(ast, dataSource) {
|
|
2639
3051
|
if (!dataSource) return void 0;
|
|
2640
3052
|
let queryBinding;
|
|
2641
|
-
(0,
|
|
3053
|
+
(0, import_traverse5.default)(ast, {
|
|
2642
3054
|
CallExpression: (nodePath) => {
|
|
2643
3055
|
const node = nodePath.node;
|
|
2644
3056
|
if (!BabelTypes.isMemberExpression(node.callee)) return;
|
|
@@ -2649,7 +3061,7 @@ var ScreenAnalyzer = class {
|
|
|
2649
3061
|
const fn = node.arguments[0];
|
|
2650
3062
|
if (!BabelTypes.isArrowFunctionExpression(fn) && !BabelTypes.isFunctionExpression(fn))
|
|
2651
3063
|
return;
|
|
2652
|
-
(0,
|
|
3064
|
+
(0, import_traverse5.default)(
|
|
2653
3065
|
fn.body,
|
|
2654
3066
|
{
|
|
2655
3067
|
noScope: true,
|
|
@@ -2695,16 +3107,16 @@ var ScreenAnalyzer = class {
|
|
|
2695
3107
|
var import_fs4 = require("fs");
|
|
2696
3108
|
var import_path4 = __toESM(require("path"));
|
|
2697
3109
|
var parser2 = __toESM(require("@babel/parser"));
|
|
2698
|
-
var
|
|
2699
|
-
var
|
|
3110
|
+
var import_traverse6 = __toESM(require("@babel/traverse"));
|
|
3111
|
+
var t8 = __toESM(require("@babel/types"));
|
|
2700
3112
|
|
|
2701
3113
|
// src/ast/navigation/module-graph.ts
|
|
2702
3114
|
var import_node_fs = require("fs");
|
|
2703
3115
|
var import_node_path = __toESM(require("path"));
|
|
2704
3116
|
var parser = __toESM(require("@babel/parser"));
|
|
2705
|
-
var
|
|
3117
|
+
var t7 = __toESM(require("@babel/types"));
|
|
2706
3118
|
var EXTENSIONS = [".tsx", ".ts", ".jsx", ".js", ".mjs", ".cjs"];
|
|
2707
|
-
var
|
|
3119
|
+
var MAX_HOPS2 = 8;
|
|
2708
3120
|
var ModuleGraph = class {
|
|
2709
3121
|
asts = /* @__PURE__ */ new Map();
|
|
2710
3122
|
resolved = /* @__PURE__ */ new Map();
|
|
@@ -2788,15 +3200,15 @@ var ModuleGraph = class {
|
|
|
2788
3200
|
const ast = this.parse(file2);
|
|
2789
3201
|
if (!ast) return out;
|
|
2790
3202
|
for (const stmt of ast.program.body) {
|
|
2791
|
-
if (!
|
|
3203
|
+
if (!t7.isImportDeclaration(stmt)) continue;
|
|
2792
3204
|
const source = stmt.source.value;
|
|
2793
3205
|
for (const spec of stmt.specifiers) {
|
|
2794
|
-
if (
|
|
3206
|
+
if (t7.isImportDefaultSpecifier(spec)) {
|
|
2795
3207
|
out.set(spec.local.name, { source, imported: "default" });
|
|
2796
|
-
} else if (
|
|
3208
|
+
} else if (t7.isImportNamespaceSpecifier(spec)) {
|
|
2797
3209
|
out.set(spec.local.name, { source, imported: "*" });
|
|
2798
|
-
} else if (
|
|
2799
|
-
const imported =
|
|
3210
|
+
} else if (t7.isImportSpecifier(spec)) {
|
|
3211
|
+
const imported = t7.isIdentifier(spec.imported) ? spec.imported.name : spec.imported.value;
|
|
2800
3212
|
out.set(spec.local.name, { source, imported });
|
|
2801
3213
|
}
|
|
2802
3214
|
}
|
|
@@ -2808,11 +3220,11 @@ var ModuleGraph = class {
|
|
|
2808
3220
|
const ast = this.parse(file2);
|
|
2809
3221
|
if (!ast) return null;
|
|
2810
3222
|
for (const stmt of ast.program.body) {
|
|
2811
|
-
const decl =
|
|
2812
|
-
if (!
|
|
3223
|
+
const decl = t7.isExportNamedDeclaration(stmt) ? stmt.declaration : stmt;
|
|
3224
|
+
if (!t7.isVariableDeclaration(decl)) continue;
|
|
2813
3225
|
for (const d of decl.declarations) {
|
|
2814
|
-
if (
|
|
2815
|
-
return
|
|
3226
|
+
if (t7.isIdentifier(d.id) && d.id.name === name && d.init) {
|
|
3227
|
+
return t7.isTSAsExpression(d.init) ? d.init.expression : d.init;
|
|
2816
3228
|
}
|
|
2817
3229
|
}
|
|
2818
3230
|
}
|
|
@@ -2823,7 +3235,7 @@ var ModuleGraph = class {
|
|
|
2823
3235
|
* Devolve o arquivo e o nome sob o qual ele é definido lá.
|
|
2824
3236
|
*/
|
|
2825
3237
|
resolveBinding(file2, name, hops = 0) {
|
|
2826
|
-
if (hops >
|
|
3238
|
+
if (hops > MAX_HOPS2) return null;
|
|
2827
3239
|
if (this.topLevelInit(file2, name) !== null) return { file: file2, name };
|
|
2828
3240
|
const binding = this.imports(file2).get(name);
|
|
2829
3241
|
if (binding) {
|
|
@@ -2836,10 +3248,10 @@ var ModuleGraph = class {
|
|
|
2836
3248
|
const ast = this.parse(file2);
|
|
2837
3249
|
if (ast) {
|
|
2838
3250
|
for (const stmt of ast.program.body) {
|
|
2839
|
-
if (!
|
|
3251
|
+
if (!t7.isExportNamedDeclaration(stmt) || !stmt.source) continue;
|
|
2840
3252
|
for (const spec of stmt.specifiers) {
|
|
2841
|
-
if (!
|
|
2842
|
-
const exported =
|
|
3253
|
+
if (!t7.isExportSpecifier(spec)) continue;
|
|
3254
|
+
const exported = t7.isIdentifier(spec.exported) ? spec.exported.name : spec.exported.value;
|
|
2843
3255
|
if (exported !== name) continue;
|
|
2844
3256
|
const target = this.resolve(file2, stmt.source.value);
|
|
2845
3257
|
if (!target) return null;
|
|
@@ -2850,7 +3262,7 @@ var ModuleGraph = class {
|
|
|
2850
3262
|
}
|
|
2851
3263
|
if (ast) {
|
|
2852
3264
|
for (const stmt of ast.program.body) {
|
|
2853
|
-
if (!
|
|
3265
|
+
if (!t7.isExportAllDeclaration(stmt)) continue;
|
|
2854
3266
|
const target = this.resolve(file2, stmt.source.value);
|
|
2855
3267
|
if (!target || target === file2) continue;
|
|
2856
3268
|
const deeper = this.resolveBinding(target, name, hops + 1);
|
|
@@ -2859,8 +3271,8 @@ var ModuleGraph = class {
|
|
|
2859
3271
|
}
|
|
2860
3272
|
if (ast) {
|
|
2861
3273
|
for (const stmt of ast.program.body) {
|
|
2862
|
-
if (!
|
|
2863
|
-
if (
|
|
3274
|
+
if (!t7.isExportDefaultDeclaration(stmt)) continue;
|
|
3275
|
+
if (t7.isIdentifier(stmt.declaration)) {
|
|
2864
3276
|
const local = stmt.declaration.name;
|
|
2865
3277
|
if (local === name) return null;
|
|
2866
3278
|
return this.resolveBinding(file2, local, hops + 1) ?? { file: file2, name: local };
|
|
@@ -2877,12 +3289,12 @@ var ModuleGraph = class {
|
|
|
2877
3289
|
* com interpolação nem valor calculado, de propósito.
|
|
2878
3290
|
*/
|
|
2879
3291
|
stringConstant(file2, node, hops = 0) {
|
|
2880
|
-
if (!node || hops >
|
|
2881
|
-
if (
|
|
2882
|
-
if (
|
|
3292
|
+
if (!node || hops > MAX_HOPS2) return null;
|
|
3293
|
+
if (t7.isStringLiteral(node)) return node.value;
|
|
3294
|
+
if (t7.isTemplateLiteral(node)) {
|
|
2883
3295
|
return node.expressions.length === 0 ? node.quasis[0]?.value.cooked ?? null : null;
|
|
2884
3296
|
}
|
|
2885
|
-
if (
|
|
3297
|
+
if (t7.isTSAsExpression(node)) return this.stringConstant(file2, node.expression, hops + 1);
|
|
2886
3298
|
const chain = memberChain(node);
|
|
2887
3299
|
if (!chain) return null;
|
|
2888
3300
|
const origin = this.resolveBinding(file2, chain.root);
|
|
@@ -2890,12 +3302,12 @@ var ModuleGraph = class {
|
|
|
2890
3302
|
let current = this.topLevelInit(origin.file, origin.name);
|
|
2891
3303
|
if (!current) return null;
|
|
2892
3304
|
for (const key of chain.path) {
|
|
2893
|
-
if (!
|
|
3305
|
+
if (!t7.isObjectExpression(current)) return null;
|
|
2894
3306
|
const prop = objectProperty(current, key);
|
|
2895
3307
|
if (!prop) return null;
|
|
2896
|
-
current =
|
|
3308
|
+
current = t7.isTSAsExpression(prop) ? prop.expression : prop;
|
|
2897
3309
|
}
|
|
2898
|
-
return
|
|
3310
|
+
return t7.isStringLiteral(current) ? current.value : null;
|
|
2899
3311
|
}
|
|
2900
3312
|
/**
|
|
2901
3313
|
* O ARQUIVO onde vive o componente de uma rota, ou `null`.
|
|
@@ -2906,24 +3318,24 @@ var ModuleGraph = class {
|
|
|
2906
3318
|
* que é como o `pocketpal` monta todas as telas do Drawer).
|
|
2907
3319
|
*/
|
|
2908
3320
|
componentFile(file2, node, hops = 0) {
|
|
2909
|
-
if (!node || hops >
|
|
2910
|
-
if (
|
|
3321
|
+
if (!node || hops > MAX_HOPS2) return null;
|
|
3322
|
+
if (t7.isCallExpression(node)) {
|
|
2911
3323
|
for (const arg of node.arguments) {
|
|
2912
|
-
if (
|
|
3324
|
+
if (t7.isIdentifier(arg) || t7.isMemberExpression(arg)) {
|
|
2913
3325
|
const inner = this.componentFile(file2, arg, hops + 1);
|
|
2914
3326
|
if (inner) return inner;
|
|
2915
3327
|
}
|
|
2916
3328
|
}
|
|
2917
3329
|
return null;
|
|
2918
3330
|
}
|
|
2919
|
-
if (
|
|
3331
|
+
if (t7.isTSAsExpression(node)) return this.componentFile(file2, node.expression, hops + 1);
|
|
2920
3332
|
const chain = memberChain(node);
|
|
2921
3333
|
if (!chain) return null;
|
|
2922
3334
|
const origin = this.resolveBinding(file2, chain.root);
|
|
2923
3335
|
if (!origin) return null;
|
|
2924
3336
|
if (chain.path.length === 0) return origin.file;
|
|
2925
3337
|
const init = this.topLevelInit(origin.file, origin.name);
|
|
2926
|
-
if (init &&
|
|
3338
|
+
if (init && t7.isObjectExpression(init)) {
|
|
2927
3339
|
const prop = objectProperty(init, chain.path[0]);
|
|
2928
3340
|
if (prop) return this.componentFile(origin.file, prop, hops + 1);
|
|
2929
3341
|
}
|
|
@@ -2946,7 +3358,7 @@ var ModuleGraph = class {
|
|
|
2946
3358
|
const names = /* @__PURE__ */ new Set();
|
|
2947
3359
|
const visit = (node) => {
|
|
2948
3360
|
if (!node || typeof node !== "object") return;
|
|
2949
|
-
if (
|
|
3361
|
+
if (t7.isJSXOpeningElement(node) && t7.isJSXIdentifier(node.name)) {
|
|
2950
3362
|
const n = node.name.name;
|
|
2951
3363
|
if (/^[A-Z]/.test(n)) names.add(n);
|
|
2952
3364
|
}
|
|
@@ -2971,24 +3383,24 @@ var ModuleGraph = class {
|
|
|
2971
3383
|
function memberChain(node) {
|
|
2972
3384
|
const chain = [];
|
|
2973
3385
|
let current = node;
|
|
2974
|
-
while (
|
|
3386
|
+
while (t7.isMemberExpression(current)) {
|
|
2975
3387
|
if (current.computed) {
|
|
2976
|
-
if (!
|
|
3388
|
+
if (!t7.isStringLiteral(current.property)) return null;
|
|
2977
3389
|
chain.unshift(current.property.value);
|
|
2978
|
-
} else if (
|
|
3390
|
+
} else if (t7.isIdentifier(current.property)) {
|
|
2979
3391
|
chain.unshift(current.property.name);
|
|
2980
3392
|
} else {
|
|
2981
3393
|
return null;
|
|
2982
3394
|
}
|
|
2983
3395
|
current = current.object;
|
|
2984
3396
|
}
|
|
2985
|
-
return
|
|
3397
|
+
return t7.isIdentifier(current) ? { root: current.name, path: chain } : null;
|
|
2986
3398
|
}
|
|
2987
3399
|
function objectProperty(obj, key) {
|
|
2988
3400
|
for (const prop of obj.properties) {
|
|
2989
|
-
if (!
|
|
2990
|
-
const name =
|
|
2991
|
-
if (name === key &&
|
|
3401
|
+
if (!t7.isObjectProperty(prop)) continue;
|
|
3402
|
+
const name = t7.isIdentifier(prop.key) ? prop.key.name : t7.isStringLiteral(prop.key) ? prop.key.value : null;
|
|
3403
|
+
if (name === key && t7.isExpression(prop.value)) return prop.value;
|
|
2992
3404
|
}
|
|
2993
3405
|
return null;
|
|
2994
3406
|
}
|
|
@@ -3102,7 +3514,7 @@ function workspaceGlobs(dir) {
|
|
|
3102
3514
|
}
|
|
3103
3515
|
function expandWorkspaceGlobs(root, globs) {
|
|
3104
3516
|
const out = [];
|
|
3105
|
-
const
|
|
3517
|
+
const add2 = (dir) => {
|
|
3106
3518
|
const pkgPath = import_node_path.default.join(dir, "package.json");
|
|
3107
3519
|
if (!(0, import_node_fs.existsSync)(pkgPath)) return;
|
|
3108
3520
|
try {
|
|
@@ -3111,9 +3523,9 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
3111
3523
|
} catch {
|
|
3112
3524
|
}
|
|
3113
3525
|
};
|
|
3114
|
-
for (const
|
|
3115
|
-
if (
|
|
3116
|
-
const parent = import_node_path.default.join(root,
|
|
3526
|
+
for (const glob2 of globs) {
|
|
3527
|
+
if (glob2.endsWith("/*")) {
|
|
3528
|
+
const parent = import_node_path.default.join(root, glob2.slice(0, -2));
|
|
3117
3529
|
let entries = [];
|
|
3118
3530
|
try {
|
|
3119
3531
|
entries = (0, import_node_fs.readdirSync)(parent);
|
|
@@ -3123,12 +3535,12 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
3123
3535
|
for (const entry of entries) {
|
|
3124
3536
|
const dir = import_node_path.default.join(parent, entry);
|
|
3125
3537
|
try {
|
|
3126
|
-
if ((0, import_node_fs.statSync)(dir).isDirectory())
|
|
3538
|
+
if ((0, import_node_fs.statSync)(dir).isDirectory()) add2(dir);
|
|
3127
3539
|
} catch {
|
|
3128
3540
|
}
|
|
3129
3541
|
}
|
|
3130
|
-
} else if (!
|
|
3131
|
-
|
|
3542
|
+
} else if (!glob2.includes("*")) {
|
|
3543
|
+
add2(import_node_path.default.join(root, glob2));
|
|
3132
3544
|
}
|
|
3133
3545
|
}
|
|
3134
3546
|
return out.sort((a, b) => b.name.length - a.name.length);
|
|
@@ -3249,19 +3661,19 @@ var NAVIGATOR_FACTORIES = {
|
|
|
3249
3661
|
};
|
|
3250
3662
|
var CUSTOM_FACTORY = /^create[A-Za-z0-9]*(Navigator|Stack|Tabs?|Drawer)$/;
|
|
3251
3663
|
function unwrapStaticScreen(value) {
|
|
3252
|
-
if (
|
|
3664
|
+
if (t8.isCallExpression(value)) {
|
|
3253
3665
|
const arg = value.arguments[0];
|
|
3254
3666
|
return arg ? unwrapStaticScreen(arg) : null;
|
|
3255
3667
|
}
|
|
3256
|
-
if (
|
|
3668
|
+
if (t8.isObjectExpression(value)) {
|
|
3257
3669
|
for (const prop of value.properties) {
|
|
3258
|
-
if (!
|
|
3259
|
-
const key =
|
|
3260
|
-
if (key === "screen" &&
|
|
3670
|
+
if (!t8.isObjectProperty(prop)) continue;
|
|
3671
|
+
const key = t8.isIdentifier(prop.key) ? prop.key.name : null;
|
|
3672
|
+
if (key === "screen" && t8.isExpression(prop.value)) return prop.value;
|
|
3261
3673
|
}
|
|
3262
3674
|
return null;
|
|
3263
3675
|
}
|
|
3264
|
-
return
|
|
3676
|
+
return t8.isExpression(value) ? value : null;
|
|
3265
3677
|
}
|
|
3266
3678
|
function inferNavigatorType(factoryName) {
|
|
3267
3679
|
if (/drawer/i.test(factoryName)) return "drawer";
|
|
@@ -3452,24 +3864,24 @@ var NavigationAnalyzer = class {
|
|
|
3452
3864
|
const ast = this.graph.parse(filePath);
|
|
3453
3865
|
if (!ast) return [];
|
|
3454
3866
|
const out = /* @__PURE__ */ new Map();
|
|
3455
|
-
(0,
|
|
3867
|
+
(0, import_traverse6.default)(ast, {
|
|
3456
3868
|
CallExpression: (nodePath) => {
|
|
3457
3869
|
const callee = nodePath.node.callee;
|
|
3458
|
-
if (!
|
|
3870
|
+
if (!t8.isIdentifier(callee)) return;
|
|
3459
3871
|
const known = NAVIGATOR_FACTORIES[callee.name] ?? (CUSTOM_FACTORY.test(callee.name) ? inferNavigatorType(callee.name) : null);
|
|
3460
3872
|
const type = known ?? inferNavigatorType(callee.name);
|
|
3461
3873
|
const confident = known !== null;
|
|
3462
3874
|
let up = nodePath.parentPath;
|
|
3463
3875
|
for (let i = 0; i < 4 && up; i++) {
|
|
3464
|
-
if (
|
|
3465
|
-
if (!
|
|
3876
|
+
if (t8.isVariableDeclarator(up.node)) break;
|
|
3877
|
+
if (!t8.isMemberExpression(up.node) && !t8.isCallExpression(up.node)) {
|
|
3466
3878
|
up = null;
|
|
3467
3879
|
break;
|
|
3468
3880
|
}
|
|
3469
3881
|
up = up.parentPath;
|
|
3470
3882
|
}
|
|
3471
3883
|
const declarator = up?.node;
|
|
3472
|
-
if (!declarator || !
|
|
3884
|
+
if (!declarator || !t8.isVariableDeclarator(declarator) || !t8.isIdentifier(declarator.id)) {
|
|
3473
3885
|
return;
|
|
3474
3886
|
}
|
|
3475
3887
|
const name = declarator.id.name;
|
|
@@ -3499,15 +3911,15 @@ var NavigationAnalyzer = class {
|
|
|
3499
3911
|
* e agrupar rotas não muda o que elas são.
|
|
3500
3912
|
*/
|
|
3501
3913
|
parseStaticScreens(filePath, config, navigatorName, navigatorType, depth = 0) {
|
|
3502
|
-
if (!config || !
|
|
3914
|
+
if (!config || !t8.isObjectExpression(config) || depth > 4) return [];
|
|
3503
3915
|
const out = [];
|
|
3504
3916
|
for (const prop of config.properties) {
|
|
3505
|
-
if (!
|
|
3506
|
-
const key =
|
|
3917
|
+
if (!t8.isObjectProperty(prop)) continue;
|
|
3918
|
+
const key = t8.isIdentifier(prop.key) ? prop.key.name : t8.isStringLiteral(prop.key) ? prop.key.value : null;
|
|
3507
3919
|
if (!key) continue;
|
|
3508
|
-
if (key === "groups" &&
|
|
3920
|
+
if (key === "groups" && t8.isObjectExpression(prop.value)) {
|
|
3509
3921
|
for (const group of prop.value.properties) {
|
|
3510
|
-
if (!
|
|
3922
|
+
if (!t8.isObjectProperty(group) || !t8.isExpression(group.value)) continue;
|
|
3511
3923
|
out.push(
|
|
3512
3924
|
...this.parseStaticScreens(
|
|
3513
3925
|
filePath,
|
|
@@ -3520,10 +3932,10 @@ var NavigationAnalyzer = class {
|
|
|
3520
3932
|
}
|
|
3521
3933
|
continue;
|
|
3522
3934
|
}
|
|
3523
|
-
if (key !== "screens" || !
|
|
3935
|
+
if (key !== "screens" || !t8.isObjectExpression(prop.value)) continue;
|
|
3524
3936
|
for (const entry of prop.value.properties) {
|
|
3525
|
-
if (!
|
|
3526
|
-
const routeName =
|
|
3937
|
+
if (!t8.isObjectProperty(entry)) continue;
|
|
3938
|
+
const routeName = t8.isIdentifier(entry.key) ? entry.key.name : t8.isStringLiteral(entry.key) ? entry.key.value : null;
|
|
3527
3939
|
if (!routeName) continue;
|
|
3528
3940
|
const componentExpr = unwrapStaticScreen(entry.value);
|
|
3529
3941
|
const componentFile = componentExpr ? this.graph.componentFile(filePath, componentExpr) : null;
|
|
@@ -3613,15 +4025,15 @@ var NavigationAnalyzer = class {
|
|
|
3613
4025
|
const ast = this.graph.parse(filePath);
|
|
3614
4026
|
if (!ast) return [];
|
|
3615
4027
|
const found = /* @__PURE__ */ new Map();
|
|
3616
|
-
(0,
|
|
4028
|
+
(0, import_traverse6.default)(ast, {
|
|
3617
4029
|
JSXElement: (nodePath) => {
|
|
3618
4030
|
const { node } = nodePath;
|
|
3619
4031
|
const openingElement = node.openingElement;
|
|
3620
|
-
if (!
|
|
4032
|
+
if (!t8.isJSXMemberExpression(openingElement.name) || !t8.isJSXIdentifier(openingElement.name.object)) {
|
|
3621
4033
|
return;
|
|
3622
4034
|
}
|
|
3623
4035
|
const objectName = openingElement.name.object.name;
|
|
3624
|
-
const propertyName =
|
|
4036
|
+
const propertyName = t8.isJSXIdentifier(openingElement.name.property) ? openingElement.name.property.name : null;
|
|
3625
4037
|
if (propertyName !== "Navigator") return;
|
|
3626
4038
|
const decl = this.resolveNavigator(filePath, objectName, declarations);
|
|
3627
4039
|
if (!decl) return;
|
|
@@ -3631,9 +4043,9 @@ var NavigationAnalyzer = class {
|
|
|
3631
4043
|
screens: []
|
|
3632
4044
|
};
|
|
3633
4045
|
const initialRouteAttr = openingElement.attributes.find(
|
|
3634
|
-
(attr) =>
|
|
4046
|
+
(attr) => t8.isJSXAttribute(attr) && t8.isJSXIdentifier(attr.name) && attr.name.name === "initialRouteName"
|
|
3635
4047
|
);
|
|
3636
|
-
if (
|
|
4048
|
+
if (t8.isJSXAttribute(initialRouteAttr)) {
|
|
3637
4049
|
const initial = this.attributeString(filePath, initialRouteAttr);
|
|
3638
4050
|
if (initial) navigator.initialRouteName = initial;
|
|
3639
4051
|
}
|
|
@@ -3685,15 +4097,15 @@ var NavigationAnalyzer = class {
|
|
|
3685
4097
|
if (!ast) return [];
|
|
3686
4098
|
const byNavigator = /* @__PURE__ */ new Map();
|
|
3687
4099
|
const resolved = /* @__PURE__ */ new Map();
|
|
3688
|
-
(0,
|
|
4100
|
+
(0, import_traverse6.default)(ast, {
|
|
3689
4101
|
JSXElement: (nodePath) => {
|
|
3690
4102
|
const name = nodePath.node.openingElement.name;
|
|
3691
|
-
if (!
|
|
3692
|
-
if (!
|
|
4103
|
+
if (!t8.isJSXMemberExpression(name) || !t8.isJSXIdentifier(name.object)) return;
|
|
4104
|
+
if (!t8.isJSXIdentifier(name.property) || name.property.name !== "Screen") return;
|
|
3693
4105
|
const insideNavigator = nodePath.findParent((parent) => {
|
|
3694
4106
|
if (!parent.isJSXElement()) return false;
|
|
3695
4107
|
const parentName = parent.node.openingElement.name;
|
|
3696
|
-
return
|
|
4108
|
+
return t8.isJSXMemberExpression(parentName) && t8.isJSXIdentifier(parentName.property) && parentName.property.name === "Navigator";
|
|
3697
4109
|
});
|
|
3698
4110
|
if (insideNavigator) return;
|
|
3699
4111
|
const local = name.object.name;
|
|
@@ -3732,9 +4144,9 @@ var NavigationAnalyzer = class {
|
|
|
3732
4144
|
const binding = nodePath.scope.getBinding(localName);
|
|
3733
4145
|
if (!binding || binding.kind !== "param") return null;
|
|
3734
4146
|
const param = binding.path.node;
|
|
3735
|
-
if (!
|
|
4147
|
+
if (!t8.isIdentifier(param) || !t8.isTSTypeAnnotation(param.typeAnnotation)) return null;
|
|
3736
4148
|
const annotation = param.typeAnnotation.typeAnnotation;
|
|
3737
|
-
if (!
|
|
4149
|
+
if (!t8.isTSTypeQuery(annotation) || !t8.isIdentifier(annotation.exprName)) return null;
|
|
3738
4150
|
return this.resolveNavigator(filePath, annotation.exprName.name, declarations);
|
|
3739
4151
|
}
|
|
3740
4152
|
/**
|
|
@@ -3755,8 +4167,8 @@ var NavigationAnalyzer = class {
|
|
|
3755
4167
|
/** O valor string de um atributo JSX, resolvendo constante importada. */
|
|
3756
4168
|
attributeString(filePath, attr) {
|
|
3757
4169
|
const value = attr.value;
|
|
3758
|
-
if (
|
|
3759
|
-
if (
|
|
4170
|
+
if (t8.isStringLiteral(value)) return value.value;
|
|
4171
|
+
if (t8.isJSXExpressionContainer(value) && t8.isExpression(value.expression)) {
|
|
3760
4172
|
return this.graph.stringConstant(filePath, value.expression);
|
|
3761
4173
|
}
|
|
3762
4174
|
return null;
|
|
@@ -3770,9 +4182,9 @@ var NavigationAnalyzer = class {
|
|
|
3770
4182
|
// `never`. It is a question about the member name, not about the node
|
|
3771
4183
|
// kind.
|
|
3772
4184
|
isNavigatorMember(node, navigatorVarName, member) {
|
|
3773
|
-
if (!
|
|
4185
|
+
if (!t8.isJSXElement(node)) return false;
|
|
3774
4186
|
const name = node.openingElement.name;
|
|
3775
|
-
return
|
|
4187
|
+
return t8.isJSXMemberExpression(name) && t8.isJSXIdentifier(name.object) && name.object.name === navigatorVarName && t8.isJSXIdentifier(name.property) && name.property.name === member;
|
|
3776
4188
|
}
|
|
3777
4189
|
/**
|
|
3778
4190
|
* Flatten a navigator's JSX children into the `<X.Screen>` elements they
|
|
@@ -3803,37 +4215,37 @@ var NavigationAnalyzer = class {
|
|
|
3803
4215
|
found.push(...this.collectScreenElements(group.children, navigatorVarName, depth + 1));
|
|
3804
4216
|
return;
|
|
3805
4217
|
}
|
|
3806
|
-
if (
|
|
4218
|
+
if (t8.isJSXElement(node)) {
|
|
3807
4219
|
const name = node.openingElement.name;
|
|
3808
|
-
const isForeignNavigator =
|
|
4220
|
+
const isForeignNavigator = t8.isJSXMemberExpression(name) && t8.isJSXIdentifier(name.property) && name.property.name === "Navigator";
|
|
3809
4221
|
if (isForeignNavigator) return;
|
|
3810
4222
|
found.push(...this.collectScreenElements(node.children, navigatorVarName, depth + 1));
|
|
3811
4223
|
return;
|
|
3812
4224
|
}
|
|
3813
|
-
if (
|
|
4225
|
+
if (t8.isJSXFragment(node)) {
|
|
3814
4226
|
found.push(...this.collectScreenElements(node.children, navigatorVarName, depth + 1));
|
|
3815
4227
|
return;
|
|
3816
4228
|
}
|
|
3817
|
-
if (
|
|
4229
|
+
if (t8.isJSXExpressionContainer(node)) {
|
|
3818
4230
|
visit(node.expression);
|
|
3819
4231
|
return;
|
|
3820
4232
|
}
|
|
3821
|
-
if (
|
|
4233
|
+
if (t8.isConditionalExpression(node)) {
|
|
3822
4234
|
visit(node.consequent);
|
|
3823
4235
|
visit(node.alternate);
|
|
3824
4236
|
return;
|
|
3825
4237
|
}
|
|
3826
|
-
if (
|
|
4238
|
+
if (t8.isLogicalExpression(node)) {
|
|
3827
4239
|
visit(node.left);
|
|
3828
4240
|
visit(node.right);
|
|
3829
4241
|
return;
|
|
3830
4242
|
}
|
|
3831
|
-
if (
|
|
4243
|
+
if (t8.isCallExpression(node)) {
|
|
3832
4244
|
for (const arg of node.arguments) {
|
|
3833
|
-
if (
|
|
3834
|
-
if (
|
|
4245
|
+
if (t8.isArrowFunctionExpression(arg) || t8.isFunctionExpression(arg)) {
|
|
4246
|
+
if (t8.isBlockStatement(arg.body)) {
|
|
3835
4247
|
for (const stmt of arg.body.body) {
|
|
3836
|
-
if (
|
|
4248
|
+
if (t8.isReturnStatement(stmt)) visit(stmt.argument);
|
|
3837
4249
|
}
|
|
3838
4250
|
} else {
|
|
3839
4251
|
visit(arg.body);
|
|
@@ -3842,11 +4254,11 @@ var NavigationAnalyzer = class {
|
|
|
3842
4254
|
}
|
|
3843
4255
|
return;
|
|
3844
4256
|
}
|
|
3845
|
-
if (
|
|
4257
|
+
if (t8.isArrayExpression(node)) {
|
|
3846
4258
|
for (const el of node.elements) visit(el);
|
|
3847
4259
|
return;
|
|
3848
4260
|
}
|
|
3849
|
-
if (
|
|
4261
|
+
if (t8.isTSAsExpression(node) || t8.isTSNonNullExpression(node)) {
|
|
3850
4262
|
visit(node.expression);
|
|
3851
4263
|
}
|
|
3852
4264
|
};
|
|
@@ -3876,17 +4288,17 @@ var NavigationAnalyzer = class {
|
|
|
3876
4288
|
parseScreenElement(filePath, element, navigatorName, navigatorType) {
|
|
3877
4289
|
const attrs = element.openingElement.attributes;
|
|
3878
4290
|
const nameAttr = attrs.find(
|
|
3879
|
-
(a) =>
|
|
4291
|
+
(a) => t8.isJSXAttribute(a) && t8.isJSXIdentifier(a.name) && a.name.name === "name"
|
|
3880
4292
|
);
|
|
3881
|
-
const screenName =
|
|
4293
|
+
const screenName = t8.isJSXAttribute(nameAttr) ? this.attributeString(filePath, nameAttr) : null;
|
|
3882
4294
|
if (!screenName) return null;
|
|
3883
4295
|
const componentAttr = attrs.find(
|
|
3884
|
-
(a) =>
|
|
4296
|
+
(a) => t8.isJSXAttribute(a) && t8.isJSXIdentifier(a.name) && a.name.name === "component"
|
|
3885
4297
|
);
|
|
3886
4298
|
let componentFile = null;
|
|
3887
|
-
if (
|
|
4299
|
+
if (t8.isJSXAttribute(componentAttr) && t8.isJSXExpressionContainer(componentAttr.value)) {
|
|
3888
4300
|
const expr = componentAttr.value.expression;
|
|
3889
|
-
if (
|
|
4301
|
+
if (t8.isExpression(expr)) componentFile = this.graph.componentFile(filePath, expr);
|
|
3890
4302
|
}
|
|
3891
4303
|
if (componentFile) this.routeTargets.set(screenName, componentFile);
|
|
3892
4304
|
return {
|
|
@@ -3899,9 +4311,9 @@ var NavigationAnalyzer = class {
|
|
|
3899
4311
|
/** Extract string attribute value from JSX attributes */
|
|
3900
4312
|
extractAttributeValue(attributes, attrName) {
|
|
3901
4313
|
const attr = attributes.find(
|
|
3902
|
-
(a) =>
|
|
4314
|
+
(a) => t8.isJSXAttribute(a) && t8.isJSXIdentifier(a.name) && a.name.name === attrName
|
|
3903
4315
|
);
|
|
3904
|
-
if (
|
|
4316
|
+
if (t8.isJSXAttribute(attr) && t8.isStringLiteral(attr.value)) {
|
|
3905
4317
|
return attr.value.value;
|
|
3906
4318
|
}
|
|
3907
4319
|
return null;
|
|
@@ -3918,7 +4330,7 @@ var NavigationAnalyzer = class {
|
|
|
3918
4330
|
...this.config.parserPlugins || []
|
|
3919
4331
|
]
|
|
3920
4332
|
});
|
|
3921
|
-
(0,
|
|
4333
|
+
(0, import_traverse6.default)(ast, {
|
|
3922
4334
|
TSTypeAliasDeclaration: (nodePath) => {
|
|
3923
4335
|
const { node } = nodePath;
|
|
3924
4336
|
const typeName = node.id.name;
|
|
@@ -3991,7 +4403,7 @@ var NavigationAnalyzer = class {
|
|
|
3991
4403
|
if (type.type === "TSUndefinedKeyword") return "undefined";
|
|
3992
4404
|
if (type.type === "TSNullKeyword") return "null";
|
|
3993
4405
|
if (type.type === "TSUnionType") {
|
|
3994
|
-
return type.types.map((
|
|
4406
|
+
return type.types.map((t18) => this.typeToString(t18)).join(" | ");
|
|
3995
4407
|
}
|
|
3996
4408
|
if (type.type === "TSTypeLiteral") {
|
|
3997
4409
|
return "object";
|
|
@@ -4012,7 +4424,7 @@ var NavigationAnalyzer = class {
|
|
|
4012
4424
|
/** Attach parsed type params to navigator screens */
|
|
4013
4425
|
attachParamsToNavigators(navigators, types) {
|
|
4014
4426
|
for (const navigator of navigators) {
|
|
4015
|
-
const matchingType = types.find((
|
|
4427
|
+
const matchingType = types.find((t18) => t18.type === navigator.type);
|
|
4016
4428
|
if (matchingType) {
|
|
4017
4429
|
for (const screen of navigator.screens) {
|
|
4018
4430
|
const screenParams = matchingType.paramEntries.get(screen.name);
|
|
@@ -4091,8 +4503,8 @@ var NavigationAnalyzer = class {
|
|
|
4091
4503
|
// src/analyzers/ComponentAnalyzer.ts
|
|
4092
4504
|
var import_fs5 = require("fs");
|
|
4093
4505
|
var parser3 = __toESM(require("@babel/parser"));
|
|
4094
|
-
var
|
|
4095
|
-
var
|
|
4506
|
+
var import_traverse7 = __toESM(require("@babel/traverse"));
|
|
4507
|
+
var t9 = __toESM(require("@babel/types"));
|
|
4096
4508
|
var ComponentAnalyzer = class {
|
|
4097
4509
|
config;
|
|
4098
4510
|
constructor(config) {
|
|
@@ -4107,7 +4519,7 @@ var ComponentAnalyzer = class {
|
|
|
4107
4519
|
plugins: ["jsx", "typescript", ["decorators", { decoratorsBeforeExport: true }]]
|
|
4108
4520
|
});
|
|
4109
4521
|
const components = [];
|
|
4110
|
-
(0,
|
|
4522
|
+
(0, import_traverse7.default)(ast, {
|
|
4111
4523
|
JSXElement: (path13) => {
|
|
4112
4524
|
const component = this.extractComponentFromJSXElement(path13.node);
|
|
4113
4525
|
if (component) {
|
|
@@ -4138,19 +4550,19 @@ var ComponentAnalyzer = class {
|
|
|
4138
4550
|
};
|
|
4139
4551
|
}
|
|
4140
4552
|
getElementName(openingElement) {
|
|
4141
|
-
if (
|
|
4553
|
+
if (t9.isJSXIdentifier(openingElement.name)) {
|
|
4142
4554
|
return openingElement.name.name;
|
|
4143
4555
|
}
|
|
4144
|
-
if (
|
|
4556
|
+
if (t9.isJSXMemberExpression(openingElement.name)) {
|
|
4145
4557
|
const parts = [];
|
|
4146
4558
|
let current = openingElement.name;
|
|
4147
|
-
while (
|
|
4148
|
-
if (
|
|
4559
|
+
while (t9.isJSXMemberExpression(current)) {
|
|
4560
|
+
if (t9.isJSXIdentifier(current.property)) {
|
|
4149
4561
|
parts.unshift(current.property.name);
|
|
4150
4562
|
}
|
|
4151
4563
|
current = current.object;
|
|
4152
4564
|
}
|
|
4153
|
-
if (
|
|
4565
|
+
if (t9.isJSXIdentifier(current)) {
|
|
4154
4566
|
parts.unshift(current.name);
|
|
4155
4567
|
}
|
|
4156
4568
|
return parts.join(".");
|
|
@@ -4173,13 +4585,13 @@ var ComponentAnalyzer = class {
|
|
|
4173
4585
|
extractProps(openingElement) {
|
|
4174
4586
|
const props = {};
|
|
4175
4587
|
for (const attr of openingElement.attributes) {
|
|
4176
|
-
if (
|
|
4588
|
+
if (t9.isJSXAttribute(attr) && t9.isJSXIdentifier(attr.name)) {
|
|
4177
4589
|
const propName = attr.name.name;
|
|
4178
4590
|
if (attr.value === null) {
|
|
4179
4591
|
props[propName] = "true";
|
|
4180
|
-
} else if (
|
|
4592
|
+
} else if (t9.isStringLiteral(attr.value)) {
|
|
4181
4593
|
props[propName] = attr.value.value;
|
|
4182
|
-
} else if (
|
|
4594
|
+
} else if (t9.isJSXExpressionContainer(attr.value) && t9.isStringLiteral(attr.value.expression)) {
|
|
4183
4595
|
props[propName] = attr.value.expression.value;
|
|
4184
4596
|
}
|
|
4185
4597
|
}
|
|
@@ -4189,12 +4601,12 @@ var ComponentAnalyzer = class {
|
|
|
4189
4601
|
extractAccessibilityProps(openingElement) {
|
|
4190
4602
|
const result = {};
|
|
4191
4603
|
for (const attr of openingElement.attributes) {
|
|
4192
|
-
if (
|
|
4604
|
+
if (t9.isJSXAttribute(attr) && t9.isJSXIdentifier(attr.name)) {
|
|
4193
4605
|
const propName = attr.name.name;
|
|
4194
4606
|
if ((propName === "testID" || propName === "accessibilityLabel") && attr.value) {
|
|
4195
|
-
if (
|
|
4607
|
+
if (t9.isStringLiteral(attr.value)) {
|
|
4196
4608
|
result[propName] = attr.value.value;
|
|
4197
|
-
} else if (
|
|
4609
|
+
} else if (t9.isJSXExpressionContainer(attr.value) && t9.isStringLiteral(attr.value.expression)) {
|
|
4198
4610
|
result[propName] = attr.value.expression.value;
|
|
4199
4611
|
}
|
|
4200
4612
|
}
|
|
@@ -4206,7 +4618,7 @@ var ComponentAnalyzer = class {
|
|
|
4206
4618
|
if (depth >= maxDepth) return [];
|
|
4207
4619
|
const extracted = [];
|
|
4208
4620
|
for (const child of children) {
|
|
4209
|
-
if (
|
|
4621
|
+
if (t9.isJSXElement(child)) {
|
|
4210
4622
|
const component = this.extractComponentFromJSXElement(child);
|
|
4211
4623
|
if (component) {
|
|
4212
4624
|
extracted.push(component);
|
|
@@ -4220,8 +4632,8 @@ var ComponentAnalyzer = class {
|
|
|
4220
4632
|
// src/analyzers/FormAnalyzer.ts
|
|
4221
4633
|
var import_fs6 = require("fs");
|
|
4222
4634
|
var parser4 = __toESM(require("@babel/parser"));
|
|
4223
|
-
var
|
|
4224
|
-
var
|
|
4635
|
+
var import_traverse8 = __toESM(require("@babel/traverse"));
|
|
4636
|
+
var t10 = __toESM(require("@babel/types"));
|
|
4225
4637
|
var path5 = __toESM(require("path"));
|
|
4226
4638
|
var FormAnalyzer = class {
|
|
4227
4639
|
config;
|
|
@@ -4242,12 +4654,12 @@ var FormAnalyzer = class {
|
|
|
4242
4654
|
this.stateVariables.clear();
|
|
4243
4655
|
this.inputElements = [];
|
|
4244
4656
|
this.submitButtons = [];
|
|
4245
|
-
(0,
|
|
4657
|
+
(0, import_traverse8.default)(ast, {
|
|
4246
4658
|
CallExpression: (path13) => {
|
|
4247
4659
|
this.extractStateVariables(path13.node);
|
|
4248
4660
|
}
|
|
4249
4661
|
});
|
|
4250
|
-
(0,
|
|
4662
|
+
(0, import_traverse8.default)(ast, {
|
|
4251
4663
|
JSXElement: (path13) => {
|
|
4252
4664
|
this.extractFormElements(path13.node);
|
|
4253
4665
|
}
|
|
@@ -4260,7 +4672,7 @@ var FormAnalyzer = class {
|
|
|
4260
4672
|
}
|
|
4261
4673
|
}
|
|
4262
4674
|
extractStateVariables(node) {
|
|
4263
|
-
if (
|
|
4675
|
+
if (t10.isIdentifier(node.callee) && node.callee.name === "useState" && node.arguments.length > 0) {
|
|
4264
4676
|
return;
|
|
4265
4677
|
}
|
|
4266
4678
|
}
|
|
@@ -4281,28 +4693,28 @@ var FormAnalyzer = class {
|
|
|
4281
4693
|
extractInputInfo(openingElement) {
|
|
4282
4694
|
const info2 = { varName: "" };
|
|
4283
4695
|
for (const attr of openingElement.attributes) {
|
|
4284
|
-
if (
|
|
4696
|
+
if (t10.isJSXAttribute(attr) && t10.isJSXIdentifier(attr.name)) {
|
|
4285
4697
|
const propName = attr.name.name;
|
|
4286
|
-
const
|
|
4698
|
+
const propValue2 = this.extractAttributeValue(attr.value);
|
|
4287
4699
|
switch (propName) {
|
|
4288
4700
|
case "label":
|
|
4289
|
-
info2.label =
|
|
4701
|
+
info2.label = propValue2;
|
|
4290
4702
|
break;
|
|
4291
4703
|
case "placeholder":
|
|
4292
|
-
info2.placeholder =
|
|
4704
|
+
info2.placeholder = propValue2;
|
|
4293
4705
|
break;
|
|
4294
4706
|
case "keyboardType":
|
|
4295
|
-
info2.keyboardType =
|
|
4707
|
+
info2.keyboardType = propValue2;
|
|
4296
4708
|
break;
|
|
4297
4709
|
case "testID":
|
|
4298
|
-
info2.testID =
|
|
4710
|
+
info2.testID = propValue2;
|
|
4299
4711
|
break;
|
|
4300
4712
|
case "appilotsId":
|
|
4301
|
-
info2.appilotsId =
|
|
4302
|
-
if (
|
|
4713
|
+
info2.appilotsId = propValue2;
|
|
4714
|
+
if (propValue2) info2.varName = propValue2;
|
|
4303
4715
|
break;
|
|
4304
4716
|
case "value":
|
|
4305
|
-
if (attr.value &&
|
|
4717
|
+
if (attr.value && t10.isJSXExpressionContainer(attr.value) && t10.isIdentifier(attr.value.expression)) {
|
|
4306
4718
|
info2.varName = attr.value.expression.name;
|
|
4307
4719
|
}
|
|
4308
4720
|
break;
|
|
@@ -4314,16 +4726,16 @@ var FormAnalyzer = class {
|
|
|
4314
4726
|
extractButtonInfo(openingElement) {
|
|
4315
4727
|
const info2 = {};
|
|
4316
4728
|
for (const attr of openingElement.attributes) {
|
|
4317
|
-
if (
|
|
4729
|
+
if (t10.isJSXAttribute(attr) && t10.isJSXIdentifier(attr.name)) {
|
|
4318
4730
|
const propName = attr.name.name;
|
|
4319
4731
|
if (propName === "title" || propName === "label") {
|
|
4320
4732
|
info2.label = this.extractAttributeValue(attr.value);
|
|
4321
4733
|
}
|
|
4322
4734
|
if (propName === "onPress") {
|
|
4323
|
-
if (attr.value &&
|
|
4324
|
-
if (
|
|
4735
|
+
if (attr.value && t10.isJSXExpressionContainer(attr.value)) {
|
|
4736
|
+
if (t10.isIdentifier(attr.value.expression)) {
|
|
4325
4737
|
info2.handler = attr.value.expression.name;
|
|
4326
|
-
} else if (
|
|
4738
|
+
} else if (t10.isArrowFunctionExpression(attr.value.expression) || t10.isFunctionExpression(attr.value.expression)) {
|
|
4327
4739
|
info2.handler = "anonymous";
|
|
4328
4740
|
}
|
|
4329
4741
|
}
|
|
@@ -4334,21 +4746,21 @@ var FormAnalyzer = class {
|
|
|
4334
4746
|
}
|
|
4335
4747
|
extractAttributeValue(value) {
|
|
4336
4748
|
if (value === null) return void 0;
|
|
4337
|
-
if (
|
|
4338
|
-
if (
|
|
4749
|
+
if (t10.isStringLiteral(value)) return value.value;
|
|
4750
|
+
if (t10.isJSXExpressionContainer(value) && t10.isStringLiteral(value.expression)) {
|
|
4339
4751
|
return value.expression.value;
|
|
4340
4752
|
}
|
|
4341
4753
|
return void 0;
|
|
4342
4754
|
}
|
|
4343
4755
|
getElementName(openingElement) {
|
|
4344
|
-
if (
|
|
4756
|
+
if (t10.isJSXIdentifier(openingElement.name)) {
|
|
4345
4757
|
return openingElement.name.name;
|
|
4346
4758
|
}
|
|
4347
4759
|
return null;
|
|
4348
4760
|
}
|
|
4349
4761
|
extractValidationRules(ast) {
|
|
4350
4762
|
const rules = {};
|
|
4351
|
-
(0,
|
|
4763
|
+
(0, import_traverse8.default)(ast, {
|
|
4352
4764
|
IfStatement: (path13) => {
|
|
4353
4765
|
const test = path13.node.test;
|
|
4354
4766
|
const rule = this.extractRuleFromCondition(test);
|
|
@@ -4365,36 +4777,36 @@ var FormAnalyzer = class {
|
|
|
4365
4777
|
extractRuleFromCondition(test) {
|
|
4366
4778
|
let fieldName = "";
|
|
4367
4779
|
let description = "";
|
|
4368
|
-
if (
|
|
4369
|
-
if (
|
|
4780
|
+
if (t10.isUnaryExpression(test) && test.operator === "!") {
|
|
4781
|
+
if (t10.isCallExpression(test.argument)) {
|
|
4370
4782
|
const callExpr = test.argument;
|
|
4371
|
-
if (
|
|
4783
|
+
if (t10.isMemberExpression(callExpr.callee)) {
|
|
4372
4784
|
const memberExpr = callExpr.callee;
|
|
4373
|
-
if (
|
|
4785
|
+
if (t10.isIdentifier(memberExpr.object)) {
|
|
4374
4786
|
fieldName = memberExpr.object.name;
|
|
4375
4787
|
description = `${fieldName} is required`;
|
|
4376
4788
|
}
|
|
4377
4789
|
}
|
|
4378
|
-
} else if (
|
|
4790
|
+
} else if (t10.isIdentifier(test.argument)) {
|
|
4379
4791
|
fieldName = test.argument.name;
|
|
4380
4792
|
description = `${fieldName} is required`;
|
|
4381
4793
|
}
|
|
4382
4794
|
}
|
|
4383
|
-
if (
|
|
4384
|
-
if (
|
|
4795
|
+
if (t10.isBinaryExpression(test) && (test.operator === "<" || test.operator === "<=")) {
|
|
4796
|
+
if (t10.isMemberExpression(test.left)) {
|
|
4385
4797
|
const memberExpr = test.left;
|
|
4386
|
-
if (
|
|
4798
|
+
if (t10.isIdentifier(memberExpr.object)) {
|
|
4387
4799
|
fieldName = memberExpr.object.name;
|
|
4388
|
-
if (
|
|
4800
|
+
if (t10.isNumericLiteral(test.right)) {
|
|
4389
4801
|
description = `${fieldName} must be at least ${test.right.value} characters`;
|
|
4390
4802
|
}
|
|
4391
4803
|
}
|
|
4392
4804
|
}
|
|
4393
4805
|
}
|
|
4394
|
-
if (
|
|
4395
|
-
if (
|
|
4396
|
-
const methodName =
|
|
4397
|
-
if (methodName === "includes" &&
|
|
4806
|
+
if (t10.isUnaryExpression(test) && test.operator === "!") {
|
|
4807
|
+
if (t10.isCallExpression(test.argument) && t10.isMemberExpression(test.argument.callee)) {
|
|
4808
|
+
const methodName = t10.isIdentifier(test.argument.callee.property) ? test.argument.callee.property.name : null;
|
|
4809
|
+
if (methodName === "includes" && t10.isIdentifier(test.argument.callee.object)) {
|
|
4398
4810
|
fieldName = test.argument.callee.object.name;
|
|
4399
4811
|
description = `${fieldName} format is invalid`;
|
|
4400
4812
|
}
|
|
@@ -4502,7 +4914,7 @@ async function composeAffordances(options) {
|
|
|
4502
4914
|
const exclusive = children.filter((c) => (fanIn.get(c) ?? 0) <= MAX_FAN_IN);
|
|
4503
4915
|
if (exclusive.length === 0) continue;
|
|
4504
4916
|
const actionIds = new Set(screen.actions.map((a) => a.id));
|
|
4505
|
-
const targetIds = new Set((screen.targets ?? []).map((
|
|
4917
|
+
const targetIds = new Set((screen.targets ?? []).map((t18) => t18.id));
|
|
4506
4918
|
const formIds = new Set(screen.forms.map((f) => f.id));
|
|
4507
4919
|
let gained = false;
|
|
4508
4920
|
for (const file2 of exclusive) {
|
|
@@ -4644,6 +5056,7 @@ var ReactNativePlatformAnalyzer = class {
|
|
|
4644
5056
|
});
|
|
4645
5057
|
return {
|
|
4646
5058
|
screens: enrichedScreens,
|
|
5059
|
+
controlEvidenceFiles: screenAnalyzer.controlEvidenceFiles,
|
|
4647
5060
|
navigation,
|
|
4648
5061
|
analyzedFiles: screenFiles.length,
|
|
4649
5062
|
...screenAnalyzer.screensFilteredOut > 0 ? { screensFilteredOut: screenAnalyzer.screensFilteredOut } : {},
|
|
@@ -4732,8 +5145,8 @@ var GenericPlatformAnalyzer = class {
|
|
|
4732
5145
|
// src/analyzers/web/WebScreenAnalyzer.ts
|
|
4733
5146
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
4734
5147
|
var import_path5 = __toESM(require("path"));
|
|
4735
|
-
var
|
|
4736
|
-
var
|
|
5148
|
+
var import_traverse10 = __toESM(require("@babel/traverse"));
|
|
5149
|
+
var t13 = __toESM(require("@babel/types"));
|
|
4737
5150
|
|
|
4738
5151
|
// src/ast/jsx/web/classify.ts
|
|
4739
5152
|
var VIEW_TAGS = /* @__PURE__ */ new Set([
|
|
@@ -4849,29 +5262,29 @@ function classifyWebJsxComponent(name, element) {
|
|
|
4849
5262
|
}
|
|
4850
5263
|
|
|
4851
5264
|
// src/ast/jsx/names.ts
|
|
4852
|
-
var
|
|
5265
|
+
var t11 = __toESM(require("@babel/types"));
|
|
4853
5266
|
function getJsxElementName(openingElement) {
|
|
4854
5267
|
return getJsxName(openingElement.name);
|
|
4855
5268
|
}
|
|
4856
5269
|
function getJsxName(name) {
|
|
4857
|
-
if (
|
|
4858
|
-
if (
|
|
4859
|
-
if (
|
|
5270
|
+
if (t11.isJSXIdentifier(name)) return name.name;
|
|
5271
|
+
if (t11.isJSXNamespacedName(name)) return `${name.namespace.name}:${name.name.name}`;
|
|
5272
|
+
if (t11.isJSXMemberExpression(name)) {
|
|
4860
5273
|
const objectName = getJsxName(name.object);
|
|
4861
|
-
const propertyName =
|
|
5274
|
+
const propertyName = t11.isJSXIdentifier(name.property) ? name.property.name : null;
|
|
4862
5275
|
return objectName && propertyName ? `${objectName}.${propertyName}` : null;
|
|
4863
5276
|
}
|
|
4864
5277
|
return null;
|
|
4865
5278
|
}
|
|
4866
5279
|
|
|
4867
5280
|
// src/ast/navigation/web-calls.ts
|
|
4868
|
-
var
|
|
4869
|
-
var
|
|
5281
|
+
var import_traverse9 = __toESM(require("@babel/traverse"));
|
|
5282
|
+
var t12 = __toESM(require("@babel/types"));
|
|
4870
5283
|
var ROUTERISH_OBJECTS = /^(router|history|navigation)$/;
|
|
4871
5284
|
function staticRoutePath(node) {
|
|
4872
5285
|
if (!node) return void 0;
|
|
4873
|
-
if (
|
|
4874
|
-
if (
|
|
5286
|
+
if (t12.isStringLiteral(node)) return node.value;
|
|
5287
|
+
if (t12.isTemplateLiteral(node)) {
|
|
4875
5288
|
let path13 = "";
|
|
4876
5289
|
node.quasis.forEach((quasi, index) => {
|
|
4877
5290
|
path13 += quasi.value.cooked ?? quasi.value.raw;
|
|
@@ -4883,15 +5296,15 @@ function staticRoutePath(node) {
|
|
|
4883
5296
|
return void 0;
|
|
4884
5297
|
}
|
|
4885
5298
|
function paramNameOf(expr) {
|
|
4886
|
-
if (
|
|
4887
|
-
if (
|
|
5299
|
+
if (t12.isIdentifier(expr)) return expr.name;
|
|
5300
|
+
if (t12.isMemberExpression(expr) && t12.isIdentifier(expr.property)) return expr.property.name;
|
|
4888
5301
|
return "param";
|
|
4889
5302
|
}
|
|
4890
5303
|
function extractWebNavigationCalls(ast) {
|
|
4891
5304
|
const calls = [];
|
|
4892
5305
|
const inspect = (node) => {
|
|
4893
|
-
if (
|
|
4894
|
-
if (
|
|
5306
|
+
if (t12.isCallExpression(node)) {
|
|
5307
|
+
if (t12.isIdentifier(node.callee) && node.callee.name === "navigate") {
|
|
4895
5308
|
const first = node.arguments[0];
|
|
4896
5309
|
const targetPath = staticRoutePath(first);
|
|
4897
5310
|
if (targetPath !== void 0) {
|
|
@@ -4899,12 +5312,12 @@ function extractWebNavigationCalls(ast) {
|
|
|
4899
5312
|
method: hasReplaceOption(node.arguments[1]) ? "replace" : "navigate",
|
|
4900
5313
|
targetPath
|
|
4901
5314
|
});
|
|
4902
|
-
} else if (
|
|
5315
|
+
} else if (t12.isNumericLiteral(first) || t12.isUnaryExpression(first) && first.operator === "-") {
|
|
4903
5316
|
calls.push({ method: "goBack" });
|
|
4904
5317
|
}
|
|
4905
5318
|
return;
|
|
4906
5319
|
}
|
|
4907
|
-
if (
|
|
5320
|
+
if (t12.isMemberExpression(node.callee) && t12.isIdentifier(node.callee.object) && ROUTERISH_OBJECTS.test(node.callee.object.name) && t12.isIdentifier(node.callee.property)) {
|
|
4908
5321
|
const method = node.callee.property.name;
|
|
4909
5322
|
const targetPath = staticRoutePath(node.arguments[0]);
|
|
4910
5323
|
if ((method === "push" || method === "navigate") && targetPath !== void 0) {
|
|
@@ -4917,25 +5330,25 @@ function extractWebNavigationCalls(ast) {
|
|
|
4917
5330
|
}
|
|
4918
5331
|
return;
|
|
4919
5332
|
}
|
|
4920
|
-
if (
|
|
5333
|
+
if (t12.isAssignmentExpression(node) && t12.isMemberExpression(node.left) && t12.isIdentifier(node.left.property) && node.left.property.name === "href" && isLocationExpression(node.left.object) && t12.isStringLiteral(node.right) && node.right.value.startsWith("/")) {
|
|
4921
5334
|
calls.push({ method: "navigate", targetPath: node.right.value });
|
|
4922
5335
|
}
|
|
4923
5336
|
};
|
|
4924
|
-
(0,
|
|
4925
|
-
noScope: !
|
|
5337
|
+
(0, import_traverse9.default)(ast, {
|
|
5338
|
+
noScope: !t12.isFile(ast),
|
|
4926
5339
|
enter: (nodePath) => inspect(nodePath.node)
|
|
4927
5340
|
});
|
|
4928
5341
|
return calls;
|
|
4929
5342
|
}
|
|
4930
5343
|
function hasReplaceOption(arg) {
|
|
4931
|
-
if (!arg || !
|
|
5344
|
+
if (!arg || !t12.isObjectExpression(arg)) return false;
|
|
4932
5345
|
return arg.properties.some(
|
|
4933
|
-
(prop) =>
|
|
5346
|
+
(prop) => t12.isObjectProperty(prop) && t12.isIdentifier(prop.key) && prop.key.name === "replace" && t12.isBooleanLiteral(prop.value) && prop.value.value === true
|
|
4934
5347
|
);
|
|
4935
5348
|
}
|
|
4936
5349
|
function isLocationExpression(node) {
|
|
4937
|
-
if (
|
|
4938
|
-
return
|
|
5350
|
+
if (t12.isIdentifier(node)) return node.name === "location";
|
|
5351
|
+
return t12.isMemberExpression(node) && t12.isIdentifier(node.object) && node.object.name === "window" && t12.isIdentifier(node.property) && node.property.name === "location";
|
|
4939
5352
|
}
|
|
4940
5353
|
|
|
4941
5354
|
// src/analyzers/web/WebScreenAnalyzer.ts
|
|
@@ -5043,7 +5456,7 @@ var WebScreenAnalyzer = class {
|
|
|
5043
5456
|
// ── registerScreen ────────────────────────────────────────────────
|
|
5044
5457
|
detectRegisterScreenCall(ast) {
|
|
5045
5458
|
let found = false;
|
|
5046
|
-
(0,
|
|
5459
|
+
(0, import_traverse10.default)(ast, {
|
|
5047
5460
|
CallExpression: (nodePath) => {
|
|
5048
5461
|
if (found) return;
|
|
5049
5462
|
if (isRegisterScreenCallee(nodePath.node.callee)) {
|
|
@@ -5056,11 +5469,11 @@ var WebScreenAnalyzer = class {
|
|
|
5056
5469
|
}
|
|
5057
5470
|
extractRegisterScreenMetadata(ast) {
|
|
5058
5471
|
let plain = null;
|
|
5059
|
-
(0,
|
|
5472
|
+
(0, import_traverse10.default)(ast, {
|
|
5060
5473
|
CallExpression: (nodePath) => {
|
|
5061
5474
|
if (!isRegisterScreenCallee(nodePath.node.callee)) return;
|
|
5062
5475
|
const arg = nodePath.node.arguments[0];
|
|
5063
|
-
if (
|
|
5476
|
+
if (t13.isObjectExpression(arg)) {
|
|
5064
5477
|
plain = literalToPlain(arg);
|
|
5065
5478
|
}
|
|
5066
5479
|
}
|
|
@@ -5104,23 +5517,23 @@ var WebScreenAnalyzer = class {
|
|
|
5104
5517
|
extractComponentName(ast) {
|
|
5105
5518
|
let defaultName = "";
|
|
5106
5519
|
let firstExported = "";
|
|
5107
|
-
(0,
|
|
5520
|
+
(0, import_traverse10.default)(ast, {
|
|
5108
5521
|
ExportDefaultDeclaration: (nodePath) => {
|
|
5109
5522
|
const declaration = nodePath.node.declaration;
|
|
5110
|
-
if (
|
|
5523
|
+
if (t13.isFunctionDeclaration(declaration) && declaration.id?.name) {
|
|
5111
5524
|
defaultName = declaration.id.name;
|
|
5112
|
-
} else if (
|
|
5525
|
+
} else if (t13.isIdentifier(declaration)) {
|
|
5113
5526
|
defaultName = declaration.name;
|
|
5114
5527
|
}
|
|
5115
5528
|
},
|
|
5116
5529
|
ExportNamedDeclaration: (nodePath) => {
|
|
5117
5530
|
if (firstExported) return;
|
|
5118
5531
|
const declaration = nodePath.node.declaration;
|
|
5119
|
-
if (
|
|
5532
|
+
if (t13.isFunctionDeclaration(declaration) && declaration.id && /^[A-Z]/.test(declaration.id.name)) {
|
|
5120
5533
|
firstExported = declaration.id.name;
|
|
5121
|
-
} else if (
|
|
5534
|
+
} else if (t13.isVariableDeclaration(declaration)) {
|
|
5122
5535
|
for (const declarator of declaration.declarations) {
|
|
5123
|
-
if (
|
|
5536
|
+
if (t13.isIdentifier(declarator.id) && /^[A-Z]/.test(declarator.id.name) && (t13.isArrowFunctionExpression(declarator.init) || t13.isFunctionExpression(declarator.init))) {
|
|
5124
5537
|
firstExported = declarator.id.name;
|
|
5125
5538
|
break;
|
|
5126
5539
|
}
|
|
@@ -5133,7 +5546,7 @@ var WebScreenAnalyzer = class {
|
|
|
5133
5546
|
extractComponents(ast) {
|
|
5134
5547
|
const components = [];
|
|
5135
5548
|
const seen = /* @__PURE__ */ new Set();
|
|
5136
|
-
(0,
|
|
5549
|
+
(0, import_traverse10.default)(ast, {
|
|
5137
5550
|
JSXOpeningElement: (nodePath) => {
|
|
5138
5551
|
const element = nodePath.node;
|
|
5139
5552
|
const name = getJsxElementName(element);
|
|
@@ -5154,7 +5567,7 @@ var WebScreenAnalyzer = class {
|
|
|
5154
5567
|
// ── <label htmlFor> association ───────────────────────────────────
|
|
5155
5568
|
collectHtmlForLabels(ast) {
|
|
5156
5569
|
const labels = /* @__PURE__ */ new Map();
|
|
5157
|
-
(0,
|
|
5570
|
+
(0, import_traverse10.default)(ast, {
|
|
5158
5571
|
JSXElement: (nodePath) => {
|
|
5159
5572
|
const element = nodePath.node;
|
|
5160
5573
|
if (getJsxElementName(element.openingElement) !== "label") return;
|
|
@@ -5197,7 +5610,7 @@ var WebScreenAnalyzer = class {
|
|
|
5197
5610
|
formBuckets.set(formElement, bucket);
|
|
5198
5611
|
return bucket;
|
|
5199
5612
|
};
|
|
5200
|
-
(0,
|
|
5613
|
+
(0, import_traverse10.default)(ast, {
|
|
5201
5614
|
JSXElement: (nodePath) => {
|
|
5202
5615
|
const element = nodePath.node;
|
|
5203
5616
|
const name = getJsxElementName(element.openingElement);
|
|
@@ -5214,7 +5627,7 @@ var WebScreenAnalyzer = class {
|
|
|
5214
5627
|
if (!bucket.fields.has(field.name)) bucket.fields.set(field.name, field);
|
|
5215
5628
|
}
|
|
5216
5629
|
});
|
|
5217
|
-
(0,
|
|
5630
|
+
(0, import_traverse10.default)(ast, {
|
|
5218
5631
|
JSXElement: (nodePath) => {
|
|
5219
5632
|
const element = nodePath.node;
|
|
5220
5633
|
const name = getJsxElementName(element.openingElement);
|
|
@@ -5292,15 +5705,15 @@ var WebScreenAnalyzer = class {
|
|
|
5292
5705
|
});
|
|
5293
5706
|
}
|
|
5294
5707
|
for (const attr of opening.attributes) {
|
|
5295
|
-
if (!
|
|
5708
|
+
if (!t13.isJSXAttribute(attr) || !t13.isJSXIdentifier(attr.name)) continue;
|
|
5296
5709
|
const attrName = attr.name.name;
|
|
5297
5710
|
if (attrName === "required") {
|
|
5298
5711
|
if (attr.value === null) field.required = true;
|
|
5299
|
-
else if (
|
|
5712
|
+
else if (t13.isJSXExpressionContainer(attr.value) && t13.isBooleanLiteral(attr.value.expression)) {
|
|
5300
5713
|
field.required = attr.value.expression.value;
|
|
5301
5714
|
}
|
|
5302
5715
|
}
|
|
5303
|
-
if ((attrName === "value" || attrName === "checked") && attr.value &&
|
|
5716
|
+
if ((attrName === "value" || attrName === "checked") && attr.value && t13.isJSXExpressionContainer(attr.value) && t13.isIdentifier(attr.value.expression)) {
|
|
5304
5717
|
field.valueBinding = attr.value.expression.name;
|
|
5305
5718
|
if (!field.name) field.name = attr.value.expression.name;
|
|
5306
5719
|
}
|
|
@@ -5350,7 +5763,7 @@ var WebScreenAnalyzer = class {
|
|
|
5350
5763
|
extractSelectOptions(selectElement) {
|
|
5351
5764
|
const options = [];
|
|
5352
5765
|
for (const child of selectElement.children) {
|
|
5353
|
-
if (!
|
|
5766
|
+
if (!t13.isJSXElement(child)) continue;
|
|
5354
5767
|
if (getJsxElementName(child.openingElement) !== "option") continue;
|
|
5355
5768
|
const value = getStringAttr(child.openingElement, "value");
|
|
5356
5769
|
const label = jsxTextContent(child) || value || "";
|
|
@@ -5392,7 +5805,7 @@ var WebScreenAnalyzer = class {
|
|
|
5392
5805
|
const actionLabels = new Map(
|
|
5393
5806
|
actions.filter((a) => a.label).map((a) => [normalizeLabel(a.label), a])
|
|
5394
5807
|
);
|
|
5395
|
-
(0,
|
|
5808
|
+
(0, import_traverse10.default)(ast, {
|
|
5396
5809
|
JSXElement: (nodePath) => {
|
|
5397
5810
|
const element = nodePath.node;
|
|
5398
5811
|
const name = getJsxElementName(element.openingElement);
|
|
@@ -5461,14 +5874,14 @@ var WebScreenAnalyzer = class {
|
|
|
5461
5874
|
}
|
|
5462
5875
|
let handlerName;
|
|
5463
5876
|
const onClickAttr = opening.attributes.find(
|
|
5464
|
-
(attr) =>
|
|
5877
|
+
(attr) => t13.isJSXAttribute(attr) && t13.isJSXIdentifier(attr.name) && attr.name.name === "onClick"
|
|
5465
5878
|
);
|
|
5466
|
-
if (onClickAttr?.value &&
|
|
5879
|
+
if (onClickAttr?.value && t13.isJSXExpressionContainer(onClickAttr.value)) {
|
|
5467
5880
|
const expr = onClickAttr.value.expression;
|
|
5468
|
-
if (
|
|
5881
|
+
if (t13.isIdentifier(expr)) {
|
|
5469
5882
|
handlerName = expr.name;
|
|
5470
5883
|
action.handler = expr.name;
|
|
5471
|
-
} else if (!
|
|
5884
|
+
} else if (!t13.isJSXEmptyExpression(expr)) {
|
|
5472
5885
|
const inlineNavCalls = extractWebNavigationCalls(expr);
|
|
5473
5886
|
const inlineNav = inlineNavCalls.find((call) => call.targetPath);
|
|
5474
5887
|
if (inlineNav?.targetPath) {
|
|
@@ -5607,12 +6020,12 @@ var WebScreenAnalyzer = class {
|
|
|
5607
6020
|
}
|
|
5608
6021
|
handlerNameFromAttr(opening, attrName) {
|
|
5609
6022
|
const attr = opening.attributes.find(
|
|
5610
|
-
(candidate) =>
|
|
6023
|
+
(candidate) => t13.isJSXAttribute(candidate) && t13.isJSXIdentifier(candidate.name) && candidate.name.name === attrName
|
|
5611
6024
|
);
|
|
5612
|
-
if (!attr?.value || !
|
|
6025
|
+
if (!attr?.value || !t13.isJSXExpressionContainer(attr.value)) return void 0;
|
|
5613
6026
|
const expr = attr.value.expression;
|
|
5614
|
-
if (
|
|
5615
|
-
if (
|
|
6027
|
+
if (t13.isIdentifier(expr)) return expr.name;
|
|
6028
|
+
if (t13.isArrowFunctionExpression(expr) || t13.isFunctionExpression(expr)) {
|
|
5616
6029
|
return firstCalledFunctionName(expr) ?? "anonymous";
|
|
5617
6030
|
}
|
|
5618
6031
|
return void 0;
|
|
@@ -5623,7 +6036,7 @@ var WebScreenAnalyzer = class {
|
|
|
5623
6036
|
for (const call of extractWebNavigationCalls(ast)) {
|
|
5624
6037
|
if (call.targetPath && call.targetPath.startsWith("/")) targets.add(call.targetPath);
|
|
5625
6038
|
}
|
|
5626
|
-
(0,
|
|
6039
|
+
(0, import_traverse10.default)(ast, {
|
|
5627
6040
|
JSXOpeningElement: (nodePath) => {
|
|
5628
6041
|
const element = nodePath.node;
|
|
5629
6042
|
const name = getJsxElementName(element);
|
|
@@ -5643,14 +6056,14 @@ var WebScreenAnalyzer = class {
|
|
|
5643
6056
|
extractCollections(ast) {
|
|
5644
6057
|
const collections = [];
|
|
5645
6058
|
const seen = /* @__PURE__ */ new Set();
|
|
5646
|
-
(0,
|
|
6059
|
+
(0, import_traverse10.default)(ast, {
|
|
5647
6060
|
JSXExpressionContainer: (nodePath) => {
|
|
5648
6061
|
const expr = nodePath.node.expression;
|
|
5649
|
-
if (!
|
|
6062
|
+
if (!t13.isCallExpression(expr) || !t13.isMemberExpression(expr.callee) || !t13.isIdentifier(expr.callee.object) || !t13.isIdentifier(expr.callee.property) || expr.callee.property.name !== "map") {
|
|
5650
6063
|
return;
|
|
5651
6064
|
}
|
|
5652
6065
|
const callback = expr.arguments[0];
|
|
5653
|
-
if (!
|
|
6066
|
+
if (!t13.isArrowFunctionExpression(callback) && !t13.isFunctionExpression(callback)) return;
|
|
5654
6067
|
const enclosing = nodePath.findParent(
|
|
5655
6068
|
(p) => p.isJSXElement()
|
|
5656
6069
|
);
|
|
@@ -5685,22 +6098,22 @@ var WebScreenAnalyzer = class {
|
|
|
5685
6098
|
}
|
|
5686
6099
|
};
|
|
5687
6100
|
function isRegisterScreenCallee(callee) {
|
|
5688
|
-
return
|
|
6101
|
+
return t13.isIdentifier(callee) && callee.name === "registerScreen" || t13.isMemberExpression(callee) && t13.isIdentifier(callee.property) && callee.property.name === "registerScreen";
|
|
5689
6102
|
}
|
|
5690
6103
|
function literalToPlain(node) {
|
|
5691
|
-
if (
|
|
6104
|
+
if (t13.isStringLiteral(node) || t13.isNumericLiteral(node) || t13.isBooleanLiteral(node)) {
|
|
5692
6105
|
return node.value;
|
|
5693
6106
|
}
|
|
5694
|
-
if (
|
|
5695
|
-
if (
|
|
5696
|
-
return node.elements.filter((el) => el !== null &&
|
|
6107
|
+
if (t13.isNullLiteral(node)) return null;
|
|
6108
|
+
if (t13.isArrayExpression(node)) {
|
|
6109
|
+
return node.elements.filter((el) => el !== null && t13.isExpression(el)).map((el) => literalToPlain(el)).filter((value) => value !== void 0);
|
|
5697
6110
|
}
|
|
5698
|
-
if (
|
|
6111
|
+
if (t13.isObjectExpression(node)) {
|
|
5699
6112
|
const out = {};
|
|
5700
6113
|
for (const prop of node.properties) {
|
|
5701
|
-
if (!
|
|
5702
|
-
const key =
|
|
5703
|
-
if (!key || !
|
|
6114
|
+
if (!t13.isObjectProperty(prop)) continue;
|
|
6115
|
+
const key = t13.isIdentifier(prop.key) ? prop.key.name : t13.isStringLiteral(prop.key) ? prop.key.value : void 0;
|
|
6116
|
+
if (!key || !t13.isExpression(prop.value)) continue;
|
|
5704
6117
|
const value = literalToPlain(prop.value);
|
|
5705
6118
|
if (value !== void 0) out[key] = value;
|
|
5706
6119
|
}
|
|
@@ -5712,12 +6125,12 @@ function jsxTextContent(element) {
|
|
|
5712
6125
|
const parts = [];
|
|
5713
6126
|
const walk = (children) => {
|
|
5714
6127
|
for (const child of children) {
|
|
5715
|
-
if (
|
|
6128
|
+
if (t13.isJSXText(child)) {
|
|
5716
6129
|
const trimmed = child.value.replace(/\s+/g, " ").trim();
|
|
5717
6130
|
if (trimmed) parts.push(trimmed);
|
|
5718
|
-
} else if (
|
|
6131
|
+
} else if (t13.isJSXExpressionContainer(child) && t13.isStringLiteral(child.expression)) {
|
|
5719
6132
|
parts.push(child.expression.value);
|
|
5720
|
-
} else if (
|
|
6133
|
+
} else if (t13.isJSXElement(child)) {
|
|
5721
6134
|
walk(child.children);
|
|
5722
6135
|
}
|
|
5723
6136
|
}
|
|
@@ -5727,26 +6140,26 @@ function jsxTextContent(element) {
|
|
|
5727
6140
|
return text.length > 0 ? text : void 0;
|
|
5728
6141
|
}
|
|
5729
6142
|
function firstCalledFunctionName(node) {
|
|
5730
|
-
if (
|
|
5731
|
-
if (
|
|
6143
|
+
if (t13.isIdentifier(node)) return node.name;
|
|
6144
|
+
if (t13.isArrowFunctionExpression(node) || t13.isFunctionExpression(node)) {
|
|
5732
6145
|
return firstCalledFunctionName(node.body);
|
|
5733
6146
|
}
|
|
5734
|
-
if (
|
|
6147
|
+
if (t13.isBlockStatement(node)) {
|
|
5735
6148
|
for (const statement of node.body) {
|
|
5736
6149
|
const handler = firstCalledFunctionName(statement);
|
|
5737
6150
|
if (handler) return handler;
|
|
5738
6151
|
}
|
|
5739
6152
|
return void 0;
|
|
5740
6153
|
}
|
|
5741
|
-
if (
|
|
5742
|
-
if (
|
|
6154
|
+
if (t13.isExpressionStatement(node)) return firstCalledFunctionName(node.expression);
|
|
6155
|
+
if (t13.isReturnStatement(node)) {
|
|
5743
6156
|
return node.argument ? firstCalledFunctionName(node.argument) : void 0;
|
|
5744
6157
|
}
|
|
5745
|
-
if (
|
|
6158
|
+
if (t13.isAwaitExpression(node) || t13.isUnaryExpression(node)) {
|
|
5746
6159
|
return firstCalledFunctionName(node.argument);
|
|
5747
6160
|
}
|
|
5748
|
-
if (
|
|
5749
|
-
if (
|
|
6161
|
+
if (t13.isCallExpression(node)) {
|
|
6162
|
+
if (t13.isIdentifier(node.callee) && !/^(navigate|confirm|alert)$/.test(node.callee.name)) {
|
|
5750
6163
|
return node.callee.name;
|
|
5751
6164
|
}
|
|
5752
6165
|
return void 0;
|
|
@@ -5755,14 +6168,14 @@ function firstCalledFunctionName(node) {
|
|
|
5755
6168
|
}
|
|
5756
6169
|
function containsWindowConfirm(body) {
|
|
5757
6170
|
let found = false;
|
|
5758
|
-
(0,
|
|
6171
|
+
(0, import_traverse10.default)(
|
|
5759
6172
|
body,
|
|
5760
6173
|
{
|
|
5761
6174
|
noScope: true,
|
|
5762
6175
|
CallExpression: (nodePath) => {
|
|
5763
6176
|
const callee = nodePath.node.callee;
|
|
5764
|
-
if (
|
|
5765
|
-
if (
|
|
6177
|
+
if (t13.isIdentifier(callee) && callee.name === "confirm") found = true;
|
|
6178
|
+
if (t13.isMemberExpression(callee) && t13.isIdentifier(callee.object) && callee.object.name === "window" && t13.isIdentifier(callee.property) && callee.property.name === "confirm") {
|
|
5766
6179
|
found = true;
|
|
5767
6180
|
}
|
|
5768
6181
|
}
|
|
@@ -5777,9 +6190,9 @@ function routePathAttr(opening, attrName) {
|
|
|
5777
6190
|
const literal = getStringAttr(opening, attrName);
|
|
5778
6191
|
if (literal) return literal;
|
|
5779
6192
|
const attr = opening.attributes.find(
|
|
5780
|
-
(candidate) =>
|
|
6193
|
+
(candidate) => t13.isJSXAttribute(candidate) && t13.isJSXIdentifier(candidate.name) && candidate.name.name === attrName
|
|
5781
6194
|
);
|
|
5782
|
-
if (!attr?.value || !
|
|
6195
|
+
if (!attr?.value || !t13.isJSXExpressionContainer(attr.value)) return void 0;
|
|
5783
6196
|
return staticRoutePath(attr.value.expression);
|
|
5784
6197
|
}
|
|
5785
6198
|
function slugify(label) {
|
|
@@ -5794,10 +6207,10 @@ function isWeakInferredFieldName(name) {
|
|
|
5794
6207
|
function collectionItemNames(callback) {
|
|
5795
6208
|
const names = /* @__PURE__ */ new Set(["item"]);
|
|
5796
6209
|
const firstParam = callback.params[0];
|
|
5797
|
-
if (
|
|
5798
|
-
if (
|
|
6210
|
+
if (t13.isIdentifier(firstParam)) names.add(firstParam.name);
|
|
6211
|
+
if (t13.isObjectPattern(firstParam)) {
|
|
5799
6212
|
for (const prop of firstParam.properties) {
|
|
5800
|
-
if (
|
|
6213
|
+
if (t13.isObjectProperty(prop) && t13.isIdentifier(prop.key) && t13.isIdentifier(prop.value)) {
|
|
5801
6214
|
names.add(prop.value.name);
|
|
5802
6215
|
}
|
|
5803
6216
|
}
|
|
@@ -5807,13 +6220,13 @@ function collectionItemNames(callback) {
|
|
|
5807
6220
|
function collectionDisplayFields(callback, itemNames) {
|
|
5808
6221
|
const fields = /* @__PURE__ */ new Set();
|
|
5809
6222
|
if (!callback.body) return [];
|
|
5810
|
-
(0,
|
|
6223
|
+
(0, import_traverse10.default)(
|
|
5811
6224
|
callback.body,
|
|
5812
6225
|
{
|
|
5813
6226
|
noScope: true,
|
|
5814
6227
|
MemberExpression: (nodePath) => {
|
|
5815
6228
|
const node = nodePath.node;
|
|
5816
|
-
if (
|
|
6229
|
+
if (t13.isIdentifier(node.object) && itemNames.has(node.object.name) && t13.isIdentifier(node.property)) {
|
|
5817
6230
|
fields.add(node.property.name);
|
|
5818
6231
|
}
|
|
5819
6232
|
}
|
|
@@ -5824,16 +6237,16 @@ function collectionDisplayFields(callback, itemNames) {
|
|
|
5824
6237
|
function collectionKeyField(callback, itemNames) {
|
|
5825
6238
|
let keyField;
|
|
5826
6239
|
if (!callback.body) return void 0;
|
|
5827
|
-
(0,
|
|
6240
|
+
(0, import_traverse10.default)(
|
|
5828
6241
|
callback.body,
|
|
5829
6242
|
{
|
|
5830
6243
|
noScope: true,
|
|
5831
6244
|
JSXAttribute: (nodePath) => {
|
|
5832
6245
|
const attr = nodePath.node;
|
|
5833
|
-
if (!
|
|
5834
|
-
if (!attr.value || !
|
|
6246
|
+
if (!t13.isJSXIdentifier(attr.name) || attr.name.name !== "key") return;
|
|
6247
|
+
if (!attr.value || !t13.isJSXExpressionContainer(attr.value)) return;
|
|
5835
6248
|
const expr = attr.value.expression;
|
|
5836
|
-
if (
|
|
6249
|
+
if (t13.isMemberExpression(expr) && t13.isIdentifier(expr.object) && itemNames.has(expr.object.name) && t13.isIdentifier(expr.property)) {
|
|
5837
6250
|
keyField = keyField ?? expr.property.name;
|
|
5838
6251
|
}
|
|
5839
6252
|
}
|
|
@@ -5856,18 +6269,18 @@ function callbackReturnsTag(callback, tags) {
|
|
|
5856
6269
|
let found = false;
|
|
5857
6270
|
const inspect = (node) => {
|
|
5858
6271
|
if (!node || found) return;
|
|
5859
|
-
if (
|
|
6272
|
+
if (t13.isJSXElement(node)) {
|
|
5860
6273
|
const name = getJsxElementName(node.openingElement);
|
|
5861
6274
|
if (name && tags.has(name)) found = true;
|
|
5862
6275
|
return;
|
|
5863
6276
|
}
|
|
5864
|
-
if (
|
|
6277
|
+
if (t13.isBlockStatement(node)) {
|
|
5865
6278
|
for (const statement of node.body) {
|
|
5866
|
-
if (
|
|
6279
|
+
if (t13.isReturnStatement(statement)) inspect(statement.argument);
|
|
5867
6280
|
}
|
|
5868
6281
|
}
|
|
5869
|
-
if (
|
|
5870
|
-
if (
|
|
6282
|
+
if (t13.isParenthesizedExpression(node)) inspect(node.expression);
|
|
6283
|
+
if (t13.isConditionalExpression(node)) {
|
|
5871
6284
|
inspect(node.consequent);
|
|
5872
6285
|
inspect(node.alternate);
|
|
5873
6286
|
}
|
|
@@ -5894,8 +6307,8 @@ function inferIdentityFields(keyField, displayFields) {
|
|
|
5894
6307
|
// src/analyzers/web/WebNavigationAnalyzer.ts
|
|
5895
6308
|
var import_fs7 = require("fs");
|
|
5896
6309
|
var import_path6 = __toESM(require("path"));
|
|
5897
|
-
var
|
|
5898
|
-
var
|
|
6310
|
+
var import_traverse11 = __toESM(require("@babel/traverse"));
|
|
6311
|
+
var t14 = __toESM(require("@babel/types"));
|
|
5899
6312
|
var WEB_NAVIGATOR_TYPE = "route";
|
|
5900
6313
|
var WebNavigationAnalyzer = class {
|
|
5901
6314
|
config;
|
|
@@ -5955,7 +6368,7 @@ var WebNavigationAnalyzer = class {
|
|
|
5955
6368
|
const name = getJsxElementName(opening);
|
|
5956
6369
|
if (name !== "Route") {
|
|
5957
6370
|
for (const child of element.children) {
|
|
5958
|
-
if (
|
|
6371
|
+
if (t14.isJSXElement(child)) visitRoute(child, parentPath);
|
|
5959
6372
|
}
|
|
5960
6373
|
return;
|
|
5961
6374
|
}
|
|
@@ -5964,16 +6377,16 @@ var WebNavigationAnalyzer = class {
|
|
|
5964
6377
|
const fullPath = this.joinPaths(parentPath, segment, isIndex);
|
|
5965
6378
|
const componentName = this.componentNameFromElementAttr(opening) ?? void 0;
|
|
5966
6379
|
const isLeaf = !element.children.some(
|
|
5967
|
-
(child) =>
|
|
6380
|
+
(child) => t14.isJSXElement(child) && getJsxElementName(child.openingElement) === "Route"
|
|
5968
6381
|
);
|
|
5969
6382
|
if ((segment || isIndex) && (componentName || isLeaf)) {
|
|
5970
6383
|
routes.push(this.buildRoute(fullPath, componentName, isIndex, !isLeaf));
|
|
5971
6384
|
}
|
|
5972
6385
|
for (const child of element.children) {
|
|
5973
|
-
if (
|
|
6386
|
+
if (t14.isJSXElement(child)) visitRoute(child, fullPath);
|
|
5974
6387
|
}
|
|
5975
6388
|
};
|
|
5976
|
-
(0,
|
|
6389
|
+
(0, import_traverse11.default)(ast, {
|
|
5977
6390
|
JSXElement: (nodePath) => {
|
|
5978
6391
|
const name = getJsxElementName(nodePath.node.openingElement);
|
|
5979
6392
|
if (name !== "Routes" && name !== "Route") return;
|
|
@@ -5992,13 +6405,13 @@ var WebNavigationAnalyzer = class {
|
|
|
5992
6405
|
/** `element={<VehicleList/>}` or `Component={VehicleList}`. */
|
|
5993
6406
|
componentNameFromElementAttr(opening) {
|
|
5994
6407
|
for (const attr of opening.attributes) {
|
|
5995
|
-
if (!
|
|
5996
|
-
if (attr.name.name === "element" &&
|
|
6408
|
+
if (!t14.isJSXAttribute(attr) || !t14.isJSXIdentifier(attr.name)) continue;
|
|
6409
|
+
if (attr.name.name === "element" && t14.isJSXExpressionContainer(attr.value)) {
|
|
5997
6410
|
const expr = attr.value.expression;
|
|
5998
|
-
if (
|
|
6411
|
+
if (t14.isJSXElement(expr)) return getJsxElementName(expr.openingElement);
|
|
5999
6412
|
}
|
|
6000
|
-
if (attr.name.name === "Component" &&
|
|
6001
|
-
if (
|
|
6413
|
+
if (attr.name.name === "Component" && t14.isJSXExpressionContainer(attr.value)) {
|
|
6414
|
+
if (t14.isIdentifier(attr.value.expression)) return attr.value.expression.name;
|
|
6002
6415
|
}
|
|
6003
6416
|
}
|
|
6004
6417
|
return null;
|
|
@@ -6012,12 +6425,12 @@ var WebNavigationAnalyzer = class {
|
|
|
6012
6425
|
"createMemoryRouter",
|
|
6013
6426
|
"useRoutes"
|
|
6014
6427
|
]);
|
|
6015
|
-
(0,
|
|
6428
|
+
(0, import_traverse11.default)(ast, {
|
|
6016
6429
|
CallExpression: (nodePath) => {
|
|
6017
6430
|
const callee = nodePath.node.callee;
|
|
6018
|
-
if (!
|
|
6431
|
+
if (!t14.isIdentifier(callee) || !ROUTER_FACTORIES.has(callee.name)) return;
|
|
6019
6432
|
const first = nodePath.node.arguments[0];
|
|
6020
|
-
if (!
|
|
6433
|
+
if (!t14.isArrayExpression(first)) return;
|
|
6021
6434
|
this.visitRouteObjects(first, "", routes);
|
|
6022
6435
|
}
|
|
6023
6436
|
});
|
|
@@ -6025,21 +6438,21 @@ var WebNavigationAnalyzer = class {
|
|
|
6025
6438
|
}
|
|
6026
6439
|
visitRouteObjects(arr, parentPath, out) {
|
|
6027
6440
|
for (const element of arr.elements) {
|
|
6028
|
-
if (!
|
|
6441
|
+
if (!t14.isObjectExpression(element)) continue;
|
|
6029
6442
|
let segment;
|
|
6030
6443
|
let isIndex = false;
|
|
6031
6444
|
let componentName;
|
|
6032
6445
|
let children;
|
|
6033
6446
|
for (const prop of element.properties) {
|
|
6034
|
-
if (!
|
|
6447
|
+
if (!t14.isObjectProperty(prop) || !t14.isIdentifier(prop.key)) continue;
|
|
6035
6448
|
const key = prop.key.name;
|
|
6036
|
-
if (key === "path" &&
|
|
6037
|
-
if (key === "index" &&
|
|
6038
|
-
if (key === "element" &&
|
|
6449
|
+
if (key === "path" && t14.isStringLiteral(prop.value)) segment = prop.value.value;
|
|
6450
|
+
if (key === "index" && t14.isBooleanLiteral(prop.value)) isIndex = prop.value.value;
|
|
6451
|
+
if (key === "element" && t14.isJSXElement(prop.value)) {
|
|
6039
6452
|
componentName = getJsxElementName(prop.value.openingElement) ?? void 0;
|
|
6040
6453
|
}
|
|
6041
|
-
if (key === "Component" &&
|
|
6042
|
-
if (key === "children" &&
|
|
6454
|
+
if (key === "Component" && t14.isIdentifier(prop.value)) componentName = prop.value.name;
|
|
6455
|
+
if (key === "children" && t14.isArrayExpression(prop.value)) children = prop.value;
|
|
6043
6456
|
}
|
|
6044
6457
|
const fullPath = this.joinPaths(parentPath, segment, isIndex);
|
|
6045
6458
|
if ((segment !== void 0 || isIndex) && (componentName || !children)) {
|
|
@@ -6200,23 +6613,23 @@ var ReactWebPlatformAnalyzer = class {
|
|
|
6200
6613
|
}
|
|
6201
6614
|
/** Replace route-path references with screen names where the route table resolves them. */
|
|
6202
6615
|
resolveRoutePaths(screen, routes) {
|
|
6203
|
-
const
|
|
6616
|
+
const resolve3 = (target) => {
|
|
6204
6617
|
if (!target || !target.startsWith("/")) return target;
|
|
6205
6618
|
return resolvePathToScreen(routes, target) ?? target;
|
|
6206
6619
|
};
|
|
6207
6620
|
const navigationTargets = Array.from(
|
|
6208
6621
|
new Set(
|
|
6209
|
-
screen.navigationTargets.map((target) =>
|
|
6622
|
+
screen.navigationTargets.map((target) => resolve3(target)).filter((target) => target !== screen.name)
|
|
6210
6623
|
)
|
|
6211
6624
|
).sort();
|
|
6212
6625
|
const actions = screen.actions.map((action) => {
|
|
6213
|
-
const resolved =
|
|
6626
|
+
const resolved = resolve3(action.targetScreen);
|
|
6214
6627
|
return resolved === action.targetScreen ? action : { ...action, targetScreen: resolved };
|
|
6215
6628
|
});
|
|
6216
6629
|
const collections = screen.collections?.map((collection) => {
|
|
6217
6630
|
const resolveRow = (row) => {
|
|
6218
6631
|
if (!row?.targetScreen) return row;
|
|
6219
|
-
const resolved =
|
|
6632
|
+
const resolved = resolve3(row.targetScreen);
|
|
6220
6633
|
if (resolved === row.targetScreen) return row;
|
|
6221
6634
|
return {
|
|
6222
6635
|
...row,
|
|
@@ -6449,8 +6862,8 @@ var ZodParsedType = util.arrayToEnum([
|
|
|
6449
6862
|
"set"
|
|
6450
6863
|
]);
|
|
6451
6864
|
var getParsedType = (data) => {
|
|
6452
|
-
const
|
|
6453
|
-
switch (
|
|
6865
|
+
const t18 = typeof data;
|
|
6866
|
+
switch (t18) {
|
|
6454
6867
|
case "undefined":
|
|
6455
6868
|
return ZodParsedType.undefined;
|
|
6456
6869
|
case "string":
|
|
@@ -10296,7 +10709,25 @@ function isSafeImageUrl(value) {
|
|
|
10296
10709
|
return SAFE_IMAGE_URL_RE.test(trimmed);
|
|
10297
10710
|
}
|
|
10298
10711
|
|
|
10299
|
-
// ../shared/dist/chunk-
|
|
10712
|
+
// ../shared/dist/chunk-KUMPS6RH.mjs
|
|
10713
|
+
var SUPPORTED_APPILOTS_LOCALES = ["pt-BR", "en", "es", "fr"];
|
|
10714
|
+
|
|
10715
|
+
// ../shared/dist/chunk-2GTRKNPJ.mjs
|
|
10716
|
+
var symbols = external_exports.array(external_exports.string().max(240)).max(12);
|
|
10717
|
+
var controlEvidenceSchema = external_exports.object({
|
|
10718
|
+
version: external_exports.literal(1),
|
|
10719
|
+
siteId: external_exports.string().regex(/^[a-f0-9]{20}$/),
|
|
10720
|
+
component: external_exports.string().max(240),
|
|
10721
|
+
icons: symbols,
|
|
10722
|
+
handler: external_exports.string().max(240).optional(),
|
|
10723
|
+
calls: symbols,
|
|
10724
|
+
argumentBindings: symbols,
|
|
10725
|
+
conditions: symbols,
|
|
10726
|
+
nativeConfirmation: external_exports.object({
|
|
10727
|
+
title: external_exports.string().max(240).optional(),
|
|
10728
|
+
destructiveOption: external_exports.boolean()
|
|
10729
|
+
}).optional()
|
|
10730
|
+
});
|
|
10300
10731
|
var locatorSourceSchema = external_exports.enum([
|
|
10301
10732
|
"appilotsId",
|
|
10302
10733
|
"testID",
|
|
@@ -10723,7 +11154,9 @@ var snapshotInputSchema = external_exports.object({
|
|
|
10723
11154
|
inModal: external_exports.boolean().optional()
|
|
10724
11155
|
}).passthrough();
|
|
10725
11156
|
var snapshotButtonSchema = external_exports.object({
|
|
11157
|
+
controlEvidence: controlEvidenceSchema.optional(),
|
|
10726
11158
|
id: boundedString(160).optional(),
|
|
11159
|
+
dispatchable: external_exports.boolean().optional(),
|
|
10727
11160
|
provenance: identityProvenanceSchema.optional(),
|
|
10728
11161
|
/**
|
|
10729
11162
|
* False when the control is mounted but currently OUTSIDE the window —
|
|
@@ -10784,7 +11217,36 @@ var snapshotListSchema = external_exports.object({
|
|
|
10784
11217
|
*/
|
|
10785
11218
|
source: boundedString(40).optional(),
|
|
10786
11219
|
itemCount: external_exports.number().int().optional(),
|
|
11220
|
+
/**
|
|
11221
|
+
* Size of the whole collection when the app declared it, for a list
|
|
11222
|
+
* that is a WINDOW onto more data than it holds.
|
|
11223
|
+
*
|
|
11224
|
+
* `itemCount` is how many rows the list is rendering from and
|
|
11225
|
+
* `visibleItemCount` how many of those are mounted; neither can
|
|
11226
|
+
* express "there are 36 and you are looking at the first 20",
|
|
11227
|
+
* because a paginated list's `data` is the page. Absent means
|
|
11228
|
+
* unknown — never "same as itemCount".
|
|
11229
|
+
*/
|
|
11230
|
+
totalItemCount: external_exports.number().int().optional(),
|
|
10787
11231
|
visibleItemCount: external_exports.number().int().optional(),
|
|
11232
|
+
viewportItemCount: external_exports.number().int().nonnegative().optional(),
|
|
11233
|
+
exploration: external_exports.object({
|
|
11234
|
+
revision: external_exports.number().int().nonnegative(),
|
|
11235
|
+
observedItemCount: external_exports.number().int().min(0).max(5e3),
|
|
11236
|
+
observedRanges: external_exports.array(
|
|
11237
|
+
external_exports.object({
|
|
11238
|
+
start: external_exports.number().int().nonnegative(),
|
|
11239
|
+
end: external_exports.number().int().nonnegative()
|
|
11240
|
+
})
|
|
11241
|
+
).max(32),
|
|
11242
|
+
rangesTruncated: external_exports.boolean(),
|
|
11243
|
+
coverage: external_exports.enum(["partial", "all-loaded"]),
|
|
11244
|
+
pagination: external_exports.enum(["possible", "not-declared"]),
|
|
11245
|
+
scrollSteps: external_exports.number().int().nonnegative(),
|
|
11246
|
+
remainingScrollSteps: external_exports.number().int().nonnegative(),
|
|
11247
|
+
consecutiveNoProgress: external_exports.number().int().nonnegative(),
|
|
11248
|
+
lastScroll: external_exports.enum(["moved", "no-progress", "boundary", "unverified"]).optional()
|
|
11249
|
+
}).optional(),
|
|
10788
11250
|
refreshing: external_exports.boolean().optional(),
|
|
10789
11251
|
empty: external_exports.boolean().optional(),
|
|
10790
11252
|
label: boundedString(300).optional(),
|
|
@@ -10832,6 +11294,7 @@ var snapshotChoiceGroupSchema = external_exports.object({
|
|
|
10832
11294
|
}).passthrough();
|
|
10833
11295
|
var snapshotElementSchema = external_exports.object({
|
|
10834
11296
|
id: boundedString(160).optional(),
|
|
11297
|
+
dispatchable: external_exports.boolean().optional(),
|
|
10835
11298
|
role: boundedString(40).optional(),
|
|
10836
11299
|
label: boundedString(300).optional(),
|
|
10837
11300
|
texts: external_exports.array(boundedString(500)).max(50).optional(),
|
|
@@ -10894,6 +11357,8 @@ var agentSnapshotSchema = external_exports.object({
|
|
|
10894
11357
|
loadingFinished: external_exports.boolean().optional(),
|
|
10895
11358
|
textsAdded: external_exports.number().int().nonnegative().optional(),
|
|
10896
11359
|
textsRemoved: external_exports.number().int().nonnegative().optional(),
|
|
11360
|
+
buttonsAddedIndices: external_exports.array(external_exports.number().int().nonnegative()).max(12).optional(),
|
|
11361
|
+
buttonsRemoved: external_exports.number().int().nonnegative().optional(),
|
|
10897
11362
|
visibleRowsDelta: external_exports.number().int().optional(),
|
|
10898
11363
|
totalRowsDelta: external_exports.number().int().optional(),
|
|
10899
11364
|
fieldsNewlyFilled: external_exports.array(boundedString(160)).max(12).optional(),
|
|
@@ -10919,6 +11384,10 @@ var agentSnapshotSchema = external_exports.object({
|
|
|
10919
11384
|
}).passthrough().optional()
|
|
10920
11385
|
}).passthrough();
|
|
10921
11386
|
var agentContextSchema = external_exports.object({
|
|
11387
|
+
missionProtocol: external_exports.literal(1).optional(),
|
|
11388
|
+
missionId: boundedString(160).optional(),
|
|
11389
|
+
/** Preferred supported device language, reported by the SDK independently of map/UI labels. */
|
|
11390
|
+
deviceLocale: external_exports.enum(SUPPORTED_APPILOTS_LOCALES).optional(),
|
|
10922
11391
|
/**
|
|
10923
11392
|
* Client platform this observation was captured on. Optional and
|
|
10924
11393
|
* additive (see `clientPlatformSchema`) — absent means
|
|
@@ -11169,6 +11638,12 @@ var actionDiagnoseSchema = external_exports.object({
|
|
|
11169
11638
|
requiresUserInput: external_exports.boolean().optional()
|
|
11170
11639
|
});
|
|
11171
11640
|
var actionResultSchema = external_exports.object({
|
|
11641
|
+
nativeConfirmation: external_exports.object({
|
|
11642
|
+
title: external_exports.string().max(300),
|
|
11643
|
+
message: external_exports.string().max(1e3).optional(),
|
|
11644
|
+
buttonLabel: external_exports.string().max(160).optional(),
|
|
11645
|
+
handlerCompleted: external_exports.boolean()
|
|
11646
|
+
}).optional(),
|
|
11172
11647
|
actionId: external_exports.string(),
|
|
11173
11648
|
type: external_exports.string(),
|
|
11174
11649
|
success: external_exports.boolean(),
|
|
@@ -11296,7 +11771,7 @@ var updateProjectBudgetSchema = external_exports.object({
|
|
|
11296
11771
|
/** null = remove webhook, undefined = leave unchanged. */
|
|
11297
11772
|
budgetWebhookUrl: external_exports.string().url().nullable().optional()
|
|
11298
11773
|
}).strict();
|
|
11299
|
-
var localeSchema = external_exports.enum(
|
|
11774
|
+
var localeSchema = external_exports.enum(SUPPORTED_APPILOTS_LOCALES);
|
|
11300
11775
|
var hexColorSchema = external_exports.string().regex(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, {
|
|
11301
11776
|
message: "Must be a hex color like #6366f1"
|
|
11302
11777
|
});
|
|
@@ -12039,9 +12514,22 @@ var MCPGenerator = class _MCPGenerator {
|
|
|
12039
12514
|
const filePath = import_node_path5.default.resolve(outputDir, `mcp-document.${this.options.format}`);
|
|
12040
12515
|
await (0, import_promises4.writeFile)(filePath, serialized, "utf-8");
|
|
12041
12516
|
console.log(`[MCPGenerator] Document written to: ${filePath}`);
|
|
12517
|
+
const controlFiles = analyzed.controlEvidenceFiles ?? {};
|
|
12518
|
+
await (0, import_promises4.writeFile)(
|
|
12519
|
+
import_node_path5.default.resolve(outputDir, "control-evidence.json"),
|
|
12520
|
+
JSON.stringify({ version: 1, files: controlFiles }, null, 2),
|
|
12521
|
+
"utf-8"
|
|
12522
|
+
);
|
|
12042
12523
|
const checksumFilePath = import_node_path5.default.resolve(outputDir, ".appilots-checksum");
|
|
12043
12524
|
await (0, import_promises4.writeFile)(checksumFilePath, checksum, "utf-8");
|
|
12044
12525
|
console.log(`[MCPGenerator] Checksum written to: ${checksumFilePath}`);
|
|
12526
|
+
const evidenceCount = Object.values(controlFiles).reduce(
|
|
12527
|
+
(sum, entries) => sum + entries.length,
|
|
12528
|
+
0
|
|
12529
|
+
);
|
|
12530
|
+
console.log(
|
|
12531
|
+
`[MCPGenerator] Source evidence: ${evidenceCount} icon controls across ${Object.keys(controlFiles).length} files (runtime binding required)`
|
|
12532
|
+
);
|
|
12045
12533
|
console.log("[MCPGenerator] Generation complete!");
|
|
12046
12534
|
return {
|
|
12047
12535
|
document,
|
|
@@ -12072,7 +12560,7 @@ var MCPGenerator = class _MCPGenerator {
|
|
|
12072
12560
|
* Calculate SHA-256 checksum of content
|
|
12073
12561
|
*/
|
|
12074
12562
|
calculateChecksum(content) {
|
|
12075
|
-
return (0,
|
|
12563
|
+
return (0, import_node_crypto2.createHash)("sha256").update(content).digest("hex");
|
|
12076
12564
|
}
|
|
12077
12565
|
/**
|
|
12078
12566
|
* Get project name and version from package.json in rootDir
|
|
@@ -12688,15 +13176,15 @@ var import_node_path9 = __toESM(require("path"));
|
|
|
12688
13176
|
var import_commander5 = require("commander");
|
|
12689
13177
|
|
|
12690
13178
|
// src/annotate/sites.ts
|
|
12691
|
-
var
|
|
12692
|
-
var
|
|
13179
|
+
var import_traverse13 = __toESM(require("@babel/traverse"));
|
|
13180
|
+
var t16 = __toESM(require("@babel/types"));
|
|
12693
13181
|
|
|
12694
13182
|
// src/annotate/forwarding.ts
|
|
12695
13183
|
var import_node_path8 = __toESM(require("path"));
|
|
12696
13184
|
var import_node_fs5 = require("fs");
|
|
12697
|
-
var
|
|
12698
|
-
var
|
|
12699
|
-
var
|
|
13185
|
+
var import_traverse12 = __toESM(require("@babel/traverse"));
|
|
13186
|
+
var t15 = __toESM(require("@babel/types"));
|
|
13187
|
+
var traverse12 = import_traverse12.default.default ?? import_traverse12.default;
|
|
12700
13188
|
var RN_TESTID_CARRIERS = /* @__PURE__ */ new Set([
|
|
12701
13189
|
"View",
|
|
12702
13190
|
"Text",
|
|
@@ -12723,14 +13211,14 @@ var RN_TESTID_CARRIERS = /* @__PURE__ */ new Set([
|
|
|
12723
13211
|
]);
|
|
12724
13212
|
function collectImports(ast) {
|
|
12725
13213
|
const out = /* @__PURE__ */ new Map();
|
|
12726
|
-
|
|
13214
|
+
traverse12(ast, {
|
|
12727
13215
|
ImportDeclaration(nodePath) {
|
|
12728
13216
|
const source = nodePath.node.source.value;
|
|
12729
13217
|
for (const spec of nodePath.node.specifiers) {
|
|
12730
|
-
if (
|
|
12731
|
-
const imported =
|
|
13218
|
+
if (t15.isImportSpecifier(spec)) {
|
|
13219
|
+
const imported = t15.isIdentifier(spec.imported) ? spec.imported.name : spec.imported.value;
|
|
12732
13220
|
out.set(spec.local.name, { source, local: spec.local.name, imported });
|
|
12733
|
-
} else if (
|
|
13221
|
+
} else if (t15.isImportDefaultSpecifier(spec)) {
|
|
12734
13222
|
out.set(spec.local.name, { source, local: spec.local.name, imported: "default" });
|
|
12735
13223
|
}
|
|
12736
13224
|
}
|
|
@@ -12778,13 +13266,13 @@ function inspectModule(file2, exportName, depth = 0) {
|
|
|
12778
13266
|
let forwards = false;
|
|
12779
13267
|
let derives = false;
|
|
12780
13268
|
let barrelTarget;
|
|
12781
|
-
|
|
13269
|
+
traverse12(ast, {
|
|
12782
13270
|
// `export { Card } from './Card'` — follow one hop.
|
|
12783
13271
|
ExportNamedDeclaration(nodePath) {
|
|
12784
13272
|
const from = nodePath.node.source?.value;
|
|
12785
13273
|
if (!from) return;
|
|
12786
13274
|
const named = nodePath.node.specifiers.some(
|
|
12787
|
-
(spec) =>
|
|
13275
|
+
(spec) => t15.isExportSpecifier(spec) && (t15.isIdentifier(spec.exported) ? spec.exported.name : spec.exported.value) === exportName
|
|
12788
13276
|
);
|
|
12789
13277
|
if (named) barrelTarget = from;
|
|
12790
13278
|
},
|
|
@@ -12793,11 +13281,11 @@ function inspectModule(file2, exportName, depth = 0) {
|
|
|
12793
13281
|
},
|
|
12794
13282
|
// `interface CardProps { testID?: string }` / `type Props = { testID… }`
|
|
12795
13283
|
TSPropertySignature(nodePath) {
|
|
12796
|
-
if (
|
|
13284
|
+
if (t15.isIdentifier(nodePath.node.key) && nodePath.node.key.name === "testID") forwards = true;
|
|
12797
13285
|
},
|
|
12798
13286
|
// `function Card({ testID }) {}` — the destructured parameter.
|
|
12799
13287
|
ObjectProperty(nodePath) {
|
|
12800
|
-
if (
|
|
13288
|
+
if (t15.isIdentifier(nodePath.node.key) && nodePath.node.key.name === "testID") forwards = true;
|
|
12801
13289
|
},
|
|
12802
13290
|
// `<View {...props} />` — anything spread onto JSX carries it along.
|
|
12803
13291
|
JSXSpreadAttribute() {
|
|
@@ -12808,17 +13296,17 @@ function inspectModule(file2, exportName, depth = 0) {
|
|
|
12808
13296
|
// that is one hardcoded name shared by every instance, which is the
|
|
12809
13297
|
// duplicate-id problem rather than a solution to it.
|
|
12810
13298
|
JSXAttribute(nodePath) {
|
|
12811
|
-
const name =
|
|
13299
|
+
const name = t15.isJSXIdentifier(nodePath.node.name) ? nodePath.node.name.name : "";
|
|
12812
13300
|
if (name !== "testID" && name !== "accessibilityLabel") return;
|
|
12813
13301
|
const value = nodePath.node.value;
|
|
12814
|
-
if (!
|
|
12815
|
-
if (
|
|
13302
|
+
if (!t15.isJSXExpressionContainer(value)) return;
|
|
13303
|
+
if (t15.isStringLiteral(value.expression)) return;
|
|
12816
13304
|
derives = true;
|
|
12817
13305
|
},
|
|
12818
13306
|
// `useAppilotsTarget(`${groupId}-${option.value}`, …)`
|
|
12819
13307
|
CallExpression(nodePath) {
|
|
12820
13308
|
const callee = nodePath.node.callee;
|
|
12821
|
-
if (
|
|
13309
|
+
if (t15.isIdentifier(callee) && IDENTITY_HOOKS.has(callee.name)) derives = true;
|
|
12822
13310
|
}
|
|
12823
13311
|
});
|
|
12824
13312
|
if (forwards || derives) return { forwards, derives };
|
|
@@ -12852,7 +13340,7 @@ function resetForwardingCache() {
|
|
|
12852
13340
|
}
|
|
12853
13341
|
|
|
12854
13342
|
// src/annotate/sites.ts
|
|
12855
|
-
var
|
|
13343
|
+
var traverse13 = import_traverse13.default.default ?? import_traverse13.default;
|
|
12856
13344
|
var LABEL_PROPS = ["title", "label", "placeholder"];
|
|
12857
13345
|
var IDENTITY_PROPS = ["testID", "appilotsId", "accessibilityLabel"];
|
|
12858
13346
|
function kebab(text) {
|
|
@@ -12903,18 +13391,18 @@ function hasLabelText(element) {
|
|
|
12903
13391
|
function namedHandler(element, prop) {
|
|
12904
13392
|
if (!prop) return void 0;
|
|
12905
13393
|
const attr = findJsxAttribute(element, prop);
|
|
12906
|
-
if (!attr || !
|
|
12907
|
-
return
|
|
13394
|
+
if (!attr || !t16.isJSXExpressionContainer(attr.value)) return void 0;
|
|
13395
|
+
return t16.isIdentifier(attr.value.expression) ? attr.value.expression.name : void 0;
|
|
12908
13396
|
}
|
|
12909
13397
|
var ANNOTATABLE_ROLES = /* @__PURE__ */ new Set(["button", "input", "select", "toggle", "date", "list"]);
|
|
12910
13398
|
function isPerItemRender(nodePath) {
|
|
12911
13399
|
let current = nodePath.parentPath;
|
|
12912
13400
|
while (current) {
|
|
12913
13401
|
const node = current.node;
|
|
12914
|
-
if (
|
|
13402
|
+
if (t16.isCallExpression(node) && t16.isMemberExpression(node.callee) && t16.isIdentifier(node.callee.property) && (node.callee.property.name === "map" || node.callee.property.name === "flatMap")) {
|
|
12915
13403
|
return true;
|
|
12916
13404
|
}
|
|
12917
|
-
if (
|
|
13405
|
+
if (t16.isJSXAttribute(node) && t16.isJSXIdentifier(node.name) && /^render[A-Z]|^ListHeaderComponent$|^ListFooterComponent$/.test(node.name.name) && node.name.name !== "renderScrollComponent") {
|
|
12918
13406
|
return /^renderItem$|^renderSectionHeader$|^renderSectionFooter$/.test(node.name.name);
|
|
12919
13407
|
}
|
|
12920
13408
|
current = current.parentPath;
|
|
@@ -12933,7 +13421,7 @@ function findAnnotationSites(source, file2) {
|
|
|
12933
13421
|
const sites = [];
|
|
12934
13422
|
const perComponent = /* @__PURE__ */ new Map();
|
|
12935
13423
|
const usedIds = /* @__PURE__ */ new Set();
|
|
12936
|
-
|
|
13424
|
+
traverse13(ast, {
|
|
12937
13425
|
JSXOpeningElement(nodePath) {
|
|
12938
13426
|
for (const prop of IDENTITY_PROPS) {
|
|
12939
13427
|
const existing = getStringAttr(nodePath.node, prop);
|
|
@@ -12941,10 +13429,10 @@ function findAnnotationSites(source, file2) {
|
|
|
12941
13429
|
}
|
|
12942
13430
|
}
|
|
12943
13431
|
});
|
|
12944
|
-
|
|
13432
|
+
traverse13(ast, {
|
|
12945
13433
|
JSXOpeningElement(nodePath) {
|
|
12946
13434
|
const element = nodePath.node;
|
|
12947
|
-
if (!
|
|
13435
|
+
if (!t16.isJSXIdentifier(element.name)) return;
|
|
12948
13436
|
const component = element.name.name;
|
|
12949
13437
|
const role = classifyJsxComponent(component, element);
|
|
12950
13438
|
if (!ANNOTATABLE_ROLES.has(role)) return;
|
|
@@ -13028,21 +13516,21 @@ function applyToFile(source, sites) {
|
|
|
13028
13516
|
// src/annotate/forward.ts
|
|
13029
13517
|
var import_node_fs6 = require("fs");
|
|
13030
13518
|
var import_magic_string2 = __toESM(require("magic-string"));
|
|
13031
|
-
var
|
|
13032
|
-
var
|
|
13033
|
-
var
|
|
13519
|
+
var import_traverse14 = __toESM(require("@babel/traverse"));
|
|
13520
|
+
var t17 = __toESM(require("@babel/types"));
|
|
13521
|
+
var traverse14 = import_traverse14.default.default ?? import_traverse14.default;
|
|
13034
13522
|
function findComponent(ast, name) {
|
|
13035
13523
|
let found;
|
|
13036
|
-
|
|
13524
|
+
traverse14(ast, {
|
|
13037
13525
|
FunctionDeclaration(nodePath) {
|
|
13038
13526
|
if (nodePath.node.id?.name === name) {
|
|
13039
13527
|
found ??= { params: nodePath.node.params, body: nodePath.node.body };
|
|
13040
13528
|
}
|
|
13041
13529
|
},
|
|
13042
13530
|
VariableDeclarator(nodePath) {
|
|
13043
|
-
if (!
|
|
13531
|
+
if (!t17.isIdentifier(nodePath.node.id) || nodePath.node.id.name !== name) return;
|
|
13044
13532
|
const init = nodePath.node.init;
|
|
13045
|
-
if (
|
|
13533
|
+
if (t17.isArrowFunctionExpression(init) || t17.isFunctionExpression(init)) {
|
|
13046
13534
|
found ??= { params: init.params, body: init.body };
|
|
13047
13535
|
}
|
|
13048
13536
|
}
|
|
@@ -13054,26 +13542,26 @@ function findReturnedRoots(body) {
|
|
|
13054
13542
|
let unsupported = false;
|
|
13055
13543
|
const record = (node) => {
|
|
13056
13544
|
if (!node) return;
|
|
13057
|
-
if (
|
|
13058
|
-
if (
|
|
13545
|
+
if (t17.isParenthesizedExpression(node)) return record(node.expression);
|
|
13546
|
+
if (t17.isJSXElement(node)) {
|
|
13059
13547
|
roots.push(node.openingElement);
|
|
13060
13548
|
return;
|
|
13061
13549
|
}
|
|
13062
|
-
if (
|
|
13550
|
+
if (t17.isConditionalExpression(node)) {
|
|
13063
13551
|
record(node.consequent);
|
|
13064
13552
|
record(node.alternate);
|
|
13065
13553
|
return;
|
|
13066
13554
|
}
|
|
13067
|
-
if (
|
|
13555
|
+
if (t17.isJSXFragment(node)) {
|
|
13068
13556
|
unsupported = true;
|
|
13069
13557
|
return;
|
|
13070
13558
|
}
|
|
13071
|
-
if (
|
|
13559
|
+
if (t17.isNullLiteral(node)) return;
|
|
13072
13560
|
unsupported = true;
|
|
13073
13561
|
};
|
|
13074
|
-
if (
|
|
13075
|
-
|
|
13076
|
-
|
|
13562
|
+
if (t17.isBlockStatement(body)) {
|
|
13563
|
+
traverse14(
|
|
13564
|
+
t17.file(t17.program([t17.expressionStatement(t17.functionExpression(null, [], body))])),
|
|
13077
13565
|
{
|
|
13078
13566
|
ReturnStatement(nodePath) {
|
|
13079
13567
|
record(nodePath.node.argument);
|
|
@@ -13087,7 +13575,7 @@ function findReturnedRoots(body) {
|
|
|
13087
13575
|
return roots;
|
|
13088
13576
|
}
|
|
13089
13577
|
function isCarrierRoot(element, imports) {
|
|
13090
|
-
if (!
|
|
13578
|
+
if (!t17.isJSXIdentifier(element.name)) return false;
|
|
13091
13579
|
const name = element.name.name;
|
|
13092
13580
|
const origin = imports.get(name);
|
|
13093
13581
|
if (origin?.source !== "react-native") return false;
|
|
@@ -13095,7 +13583,7 @@ function isCarrierRoot(element, imports) {
|
|
|
13095
13583
|
}
|
|
13096
13584
|
function hasProp(element, name) {
|
|
13097
13585
|
return element.attributes.some(
|
|
13098
|
-
(attr) =>
|
|
13586
|
+
(attr) => t17.isJSXAttribute(attr) && t17.isJSXIdentifier(attr.name) && attr.name.name === name
|
|
13099
13587
|
);
|
|
13100
13588
|
}
|
|
13101
13589
|
function indentOf(source, offset) {
|
|
@@ -13121,12 +13609,12 @@ ${indent}${text}` };
|
|
|
13121
13609
|
}
|
|
13122
13610
|
function followBarrel(file2, ast, component) {
|
|
13123
13611
|
let target;
|
|
13124
|
-
|
|
13612
|
+
traverse14(ast, {
|
|
13125
13613
|
ExportNamedDeclaration(nodePath) {
|
|
13126
13614
|
const from = nodePath.node.source?.value;
|
|
13127
13615
|
if (!from) return;
|
|
13128
13616
|
const named = nodePath.node.specifiers.some(
|
|
13129
|
-
(spec) =>
|
|
13617
|
+
(spec) => t17.isExportSpecifier(spec) && (t17.isIdentifier(spec.exported) ? spec.exported.name : spec.exported.value) === component
|
|
13130
13618
|
);
|
|
13131
13619
|
if (named) target ??= from;
|
|
13132
13620
|
}
|
|
@@ -13154,7 +13642,7 @@ function planForward(file2, component, depth = 0) {
|
|
|
13154
13642
|
return { ...base, refusal: `no function named ${component} in this file` };
|
|
13155
13643
|
}
|
|
13156
13644
|
const param = found.params[0];
|
|
13157
|
-
if (!
|
|
13645
|
+
if (!t17.isObjectPattern(param)) {
|
|
13158
13646
|
return {
|
|
13159
13647
|
...base,
|
|
13160
13648
|
refusal: `${component} does not destructure its props, so there is no safe place to add one without knowing what the parameter is called downstream`
|
|
@@ -13170,7 +13658,7 @@ function planForward(file2, component, depth = 0) {
|
|
|
13170
13658
|
const imports = collectImports(ast);
|
|
13171
13659
|
const nonCarrier = roots.find((root) => !isCarrierRoot(root, imports));
|
|
13172
13660
|
if (nonCarrier) {
|
|
13173
|
-
const name =
|
|
13661
|
+
const name = t17.isJSXIdentifier(nonCarrier.name) ? nonCarrier.name.name : "its root";
|
|
13174
13662
|
return {
|
|
13175
13663
|
...base,
|
|
13176
13664
|
refusal: `${component} renders <${name}> at its root, which is not a React Native element known to accept testID. Forwarding into it would move the problem down a level, not fix it`
|
|
@@ -13178,30 +13666,30 @@ function planForward(file2, component, depth = 0) {
|
|
|
13178
13666
|
}
|
|
13179
13667
|
const edits = [];
|
|
13180
13668
|
const alreadyDestructured = param.properties.some(
|
|
13181
|
-
(prop) =>
|
|
13669
|
+
(prop) => t17.isObjectProperty(prop) && t17.isIdentifier(prop.key) && prop.key.name === "testID"
|
|
13182
13670
|
);
|
|
13183
13671
|
if (!alreadyDestructured) {
|
|
13184
13672
|
const insert = insertAfterLastMember(source, param.properties, "testID", param.end ?? 0, ",");
|
|
13185
13673
|
edits.push({ ...insert, what: "parameter" });
|
|
13186
13674
|
}
|
|
13187
13675
|
const annotation = param.typeAnnotation;
|
|
13188
|
-
if (
|
|
13676
|
+
if (t17.isTSTypeAnnotation(annotation)) {
|
|
13189
13677
|
const declared = annotation.typeAnnotation;
|
|
13190
13678
|
let body;
|
|
13191
13679
|
let closing = 0;
|
|
13192
|
-
if (
|
|
13680
|
+
if (t17.isTSTypeLiteral(declared)) {
|
|
13193
13681
|
body = declared.members;
|
|
13194
13682
|
closing = declared.end ?? 0;
|
|
13195
|
-
} else if (
|
|
13683
|
+
} else if (t17.isTSTypeReference(declared) && t17.isIdentifier(declared.typeName)) {
|
|
13196
13684
|
const typeName = declared.typeName.name;
|
|
13197
|
-
|
|
13685
|
+
traverse14(ast, {
|
|
13198
13686
|
TSInterfaceDeclaration(nodePath) {
|
|
13199
13687
|
if (nodePath.node.id.name !== typeName) return;
|
|
13200
13688
|
body = nodePath.node.body.body;
|
|
13201
13689
|
closing = nodePath.node.body.end ?? 0;
|
|
13202
13690
|
},
|
|
13203
13691
|
TSTypeAliasDeclaration(nodePath) {
|
|
13204
|
-
if (nodePath.node.id.name !== typeName || !
|
|
13692
|
+
if (nodePath.node.id.name !== typeName || !t17.isTSTypeLiteral(nodePath.node.typeAnnotation))
|
|
13205
13693
|
return;
|
|
13206
13694
|
body = nodePath.node.typeAnnotation.members;
|
|
13207
13695
|
closing = nodePath.node.typeAnnotation.end ?? 0;
|
|
@@ -13216,7 +13704,7 @@ function planForward(file2, component, depth = 0) {
|
|
|
13216
13704
|
}
|
|
13217
13705
|
if (body) {
|
|
13218
13706
|
const declaresTestId = body.some(
|
|
13219
|
-
(member) =>
|
|
13707
|
+
(member) => t17.isTSPropertySignature(member) && t17.isIdentifier(member.key) && member.key.name === "testID"
|
|
13220
13708
|
);
|
|
13221
13709
|
if (!declaresTestId) {
|
|
13222
13710
|
const insert = insertAfterLastMember(source, body, "testID?: string;", closing, ";");
|
|
@@ -14009,9 +14497,145 @@ Pass rate: ${passed}/${total} (${passRatePct}%) \u2014 threshold ${minPassRatePc
|
|
|
14009
14497
|
});
|
|
14010
14498
|
}
|
|
14011
14499
|
|
|
14500
|
+
// src/cli/commands/knowledge.ts
|
|
14501
|
+
var import_commander8 = require("commander");
|
|
14502
|
+
var import_promises7 = require("fs/promises");
|
|
14503
|
+
var import_node_path10 = require("path");
|
|
14504
|
+
var import_node_crypto3 = require("crypto");
|
|
14505
|
+
var import_fast_glob2 = __toESM(require("fast-glob"));
|
|
14506
|
+
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".txt", ".html", ".htm", ".pdf"]);
|
|
14507
|
+
var MIME_MAP = {
|
|
14508
|
+
".md": "text/markdown",
|
|
14509
|
+
".txt": "text/plain",
|
|
14510
|
+
".html": "text/html",
|
|
14511
|
+
".htm": "text/html",
|
|
14512
|
+
".pdf": "application/pdf"
|
|
14513
|
+
};
|
|
14514
|
+
function knowledgeCommand() {
|
|
14515
|
+
const cmd = new import_commander8.Command("knowledge").description("Manage the project knowledge base (RAG)");
|
|
14516
|
+
cmd.command("sync").description("Sync local documentation files to the knowledge base").option("--verbose", "Enable verbose logging", false).option("--api-key <key>", "Appilots API key (overrides .appilotsrc)").option("--server <url>", "Appilots server URL (overrides .appilotsrc)").option("--json", "Output a machine-readable JSON result (implies --quiet)", false).option("--quiet", "Suppress banner and informational output", false).action(async (options) => {
|
|
14517
|
+
if (options.json) setLogMode("json");
|
|
14518
|
+
else if (options.quiet) setLogMode("quiet");
|
|
14519
|
+
try {
|
|
14520
|
+
banner();
|
|
14521
|
+
const loaded = loadConfig(warn);
|
|
14522
|
+
const apiKey = options.apiKey || loaded?.apiKey;
|
|
14523
|
+
if (!apiKey) {
|
|
14524
|
+
error("No .appilotsrc configuration found");
|
|
14525
|
+
info("Run: appilots init --api-key <your-key>");
|
|
14526
|
+
jsonOutput({ success: false, error: "No configuration found (missing API key)" });
|
|
14527
|
+
process.exit(1);
|
|
14528
|
+
return;
|
|
14529
|
+
}
|
|
14530
|
+
const config = {
|
|
14531
|
+
serverUrl: DEFAULT_SERVER_URL,
|
|
14532
|
+
...loaded,
|
|
14533
|
+
apiKey,
|
|
14534
|
+
...options.server ? { serverUrl: options.server } : {}
|
|
14535
|
+
};
|
|
14536
|
+
const sources = config.knowledge?.sources;
|
|
14537
|
+
if (!sources || sources.length === 0) {
|
|
14538
|
+
error("No knowledge sources configured");
|
|
14539
|
+
info("Add knowledge.sources to .appilotsrc:");
|
|
14540
|
+
info(' { "knowledge": { "sources": ["docs/**/*.md"] } }');
|
|
14541
|
+
jsonOutput({ success: false, error: "No knowledge sources configured" });
|
|
14542
|
+
process.exit(1);
|
|
14543
|
+
return;
|
|
14544
|
+
}
|
|
14545
|
+
const spinner = createSpinner("Scanning for knowledge documents...");
|
|
14546
|
+
spinner.start();
|
|
14547
|
+
const rootDir = process.cwd();
|
|
14548
|
+
const excludePatterns = config.knowledge?.exclude ?? [];
|
|
14549
|
+
const allFiles = [];
|
|
14550
|
+
for (const pattern of sources) {
|
|
14551
|
+
const matches = await (0, import_fast_glob2.default)(pattern, {
|
|
14552
|
+
cwd: rootDir,
|
|
14553
|
+
ignore: ["node_modules/**", ...excludePatterns],
|
|
14554
|
+
onlyFiles: true
|
|
14555
|
+
});
|
|
14556
|
+
for (const match of matches) {
|
|
14557
|
+
const ext = (0, import_node_path10.extname)(match).toLowerCase();
|
|
14558
|
+
if (SUPPORTED_EXTENSIONS.has(ext) && !allFiles.includes(match)) {
|
|
14559
|
+
allFiles.push(match);
|
|
14560
|
+
}
|
|
14561
|
+
}
|
|
14562
|
+
}
|
|
14563
|
+
spinner.stop();
|
|
14564
|
+
if (allFiles.length === 0) {
|
|
14565
|
+
warn("No supported files found matching knowledge.sources patterns");
|
|
14566
|
+
info(`Supported: ${[...SUPPORTED_EXTENSIONS].join(", ")}`);
|
|
14567
|
+
jsonOutput({ success: true, uploaded: 0, skipped: 0, errors: 0, files: 0 });
|
|
14568
|
+
return;
|
|
14569
|
+
}
|
|
14570
|
+
info(`Found ${allFiles.length} document${allFiles.length === 1 ? "" : "s"}`);
|
|
14571
|
+
const documents = [];
|
|
14572
|
+
for (const filePath of allFiles) {
|
|
14573
|
+
const absolutePath = (0, import_node_path10.resolve)(rootDir, filePath);
|
|
14574
|
+
const buffer = await (0, import_promises7.readFile)(absolutePath);
|
|
14575
|
+
const checksum = (0, import_node_crypto3.createHash)("sha256").update(buffer).digest("hex");
|
|
14576
|
+
const ext = (0, import_node_path10.extname)(filePath).toLowerCase();
|
|
14577
|
+
const mimeType = MIME_MAP[ext] ?? "text/plain";
|
|
14578
|
+
documents.push({
|
|
14579
|
+
filename: (0, import_node_path10.relative)(rootDir, absolutePath),
|
|
14580
|
+
content: buffer.toString("base64"),
|
|
14581
|
+
mimeType,
|
|
14582
|
+
checksum
|
|
14583
|
+
});
|
|
14584
|
+
}
|
|
14585
|
+
const uploadSpinner = createSpinner(
|
|
14586
|
+
`Uploading ${documents.length} document${documents.length === 1 ? "" : "s"}...`
|
|
14587
|
+
);
|
|
14588
|
+
uploadSpinner.start();
|
|
14589
|
+
const apiClient = new AppilotsAPIClient({
|
|
14590
|
+
serverUrl: config.serverUrl,
|
|
14591
|
+
apiKey: config.apiKey,
|
|
14592
|
+
timeoutMs: 12e4
|
|
14593
|
+
// Knowledge sync can take longer (embedding)
|
|
14594
|
+
});
|
|
14595
|
+
const result = await apiClient.knowledgeSync(documents);
|
|
14596
|
+
uploadSpinner.stop();
|
|
14597
|
+
if (!result.success) {
|
|
14598
|
+
error(`Knowledge sync failed: ${result.error}`);
|
|
14599
|
+
jsonOutput({ success: false, error: result.error });
|
|
14600
|
+
process.exit(1);
|
|
14601
|
+
return;
|
|
14602
|
+
}
|
|
14603
|
+
success(
|
|
14604
|
+
`Knowledge sync complete: ${result.uploaded} uploaded, ${result.skipped} skipped, ${result.errors} errors`
|
|
14605
|
+
);
|
|
14606
|
+
if (options.verbose && result.details) {
|
|
14607
|
+
for (const detail of result.details) {
|
|
14608
|
+
if (detail.status === "uploaded") {
|
|
14609
|
+
info(` + ${detail.filename}`);
|
|
14610
|
+
} else if (detail.status === "skipped") {
|
|
14611
|
+
info(` = ${detail.filename} (unchanged)`);
|
|
14612
|
+
} else {
|
|
14613
|
+
warn(` ! ${detail.filename}: ${detail.error}`);
|
|
14614
|
+
}
|
|
14615
|
+
}
|
|
14616
|
+
}
|
|
14617
|
+
jsonOutput({
|
|
14618
|
+
success: true,
|
|
14619
|
+
uploaded: result.uploaded,
|
|
14620
|
+
skipped: result.skipped,
|
|
14621
|
+
errors: result.errors,
|
|
14622
|
+
details: result.details
|
|
14623
|
+
});
|
|
14624
|
+
} catch (err) {
|
|
14625
|
+
error(`Knowledge sync failed: ${stringifyUnknown(err)}`);
|
|
14626
|
+
jsonOutput({ success: false, error: stringifyUnknown(err) });
|
|
14627
|
+
if (options.verbose) {
|
|
14628
|
+
console.error(err);
|
|
14629
|
+
}
|
|
14630
|
+
process.exit(1);
|
|
14631
|
+
}
|
|
14632
|
+
});
|
|
14633
|
+
return cmd;
|
|
14634
|
+
}
|
|
14635
|
+
|
|
14012
14636
|
// src/cli/index.ts
|
|
14013
14637
|
async function main() {
|
|
14014
|
-
const program2 = new
|
|
14638
|
+
const program2 = new import_commander9.Command();
|
|
14015
14639
|
program2.name("appilots").description(
|
|
14016
14640
|
"Analyze your React Native app and generate MCP documents for AI-powered navigation.\n\nQuick start:\n $ appilots init --api-key ak_xxx\n $ appilots sync\n $ appilots watch"
|
|
14017
14641
|
).version(CLI_VERSION).option("--verbose", "Enable verbose logging");
|
|
@@ -14022,6 +14646,7 @@ async function main() {
|
|
|
14022
14646
|
program2.addCommand(annotateCommand());
|
|
14023
14647
|
program2.addCommand(statusCommand());
|
|
14024
14648
|
program2.addCommand(evalCommand());
|
|
14649
|
+
program2.addCommand(knowledgeCommand());
|
|
14025
14650
|
const args = process.argv.slice(2);
|
|
14026
14651
|
const firstArg = args[0];
|
|
14027
14652
|
const hasCommand = firstArg !== void 0 && args.length > 0 && !firstArg.startsWith("-");
|