@hyphaene/hexa-ts-kit 1.12.1 → 1.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.
@@ -1043,7 +1043,7 @@ function getVaultConfig(config) {
1043
1043
 
1044
1044
  // src/lib/contracts/permissions-extractor.ts
1045
1045
  import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
1046
- import { parse } from "@typescript-eslint/typescript-estree";
1046
+ import { Project, SyntaxKind } from "ts-morph";
1047
1047
  function extractValues(sourcePath, config) {
1048
1048
  if (!existsSync3(sourcePath)) {
1049
1049
  return {
@@ -1062,12 +1062,13 @@ function extractValues(sourcePath, config) {
1062
1062
  sourcePath
1063
1063
  };
1064
1064
  }
1065
- let ast;
1065
+ const project = new Project({
1066
+ useInMemoryFileSystem: true,
1067
+ skipAddingFilesFromTsConfig: true
1068
+ });
1069
+ let sourceFile;
1066
1070
  try {
1067
- ast = parse(content, {
1068
- loc: true,
1069
- range: true
1070
- });
1071
+ sourceFile = project.createSourceFile("temp.ts", content);
1071
1072
  } catch (err) {
1072
1073
  return {
1073
1074
  success: false,
@@ -1078,9 +1079,9 @@ function extractValues(sourcePath, config) {
1078
1079
  const exportName = config.export;
1079
1080
  const extractMode = config.extract;
1080
1081
  if (extractMode === "const-values") {
1081
- return extractConstValues(ast, exportName, sourcePath);
1082
+ return extractConstValues(sourceFile, exportName, sourcePath);
1082
1083
  } else if (extractMode === "zod-keys") {
1083
- return extractZodKeys(ast, exportName, sourcePath);
1084
+ return extractZodKeys(sourceFile, exportName, sourcePath);
1084
1085
  }
1085
1086
  return {
1086
1087
  success: false,
@@ -1088,33 +1089,34 @@ function extractValues(sourcePath, config) {
1088
1089
  sourcePath
1089
1090
  };
1090
1091
  }
1091
- function extractConstValues(ast, exportName, sourcePath) {
1092
+ function extractConstValues(sourceFile, exportName, sourcePath) {
1092
1093
  const values = [];
1093
1094
  const keyToValue = {};
1094
- for (const node of ast.body) {
1095
- if (node.type === "ExportNamedDeclaration" && node.declaration?.type === "VariableDeclaration") {
1096
- for (const declarator of node.declaration.declarations) {
1097
- if (declarator.id.type === "Identifier" && declarator.id.name === exportName && declarator.init) {
1098
- let objectExpr = null;
1099
- if (declarator.init.type === "TSAsExpression") {
1100
- const expr = declarator.init.expression;
1101
- if (expr.type === "ObjectExpression") {
1102
- objectExpr = expr;
1103
- }
1104
- } else if (declarator.init.type === "ObjectExpression") {
1105
- objectExpr = declarator.init;
1106
- }
1107
- if (objectExpr) {
1108
- for (const prop of objectExpr.properties) {
1109
- if (prop.type === "Property" && prop.value.type === "Literal" && typeof prop.value.value === "string") {
1110
- const value = prop.value.value;
1111
- values.push(value);
1112
- if (prop.key.type === "Identifier") {
1113
- keyToValue[prop.key.name] = value;
1114
- }
1115
- }
1116
- }
1117
- }
1095
+ for (const varStmt of sourceFile.getVariableStatements()) {
1096
+ if (!varStmt.isExported()) continue;
1097
+ for (const decl of varStmt.getDeclarations()) {
1098
+ if (decl.getName() !== exportName) continue;
1099
+ const init = decl.getInitializer();
1100
+ if (!init) continue;
1101
+ let objLiteral = init.asKind(SyntaxKind.ObjectLiteralExpression);
1102
+ if (!objLiteral) {
1103
+ const asExpr = init.asKind(SyntaxKind.AsExpression);
1104
+ if (asExpr) {
1105
+ objLiteral = asExpr.getExpression().asKind(SyntaxKind.ObjectLiteralExpression);
1106
+ }
1107
+ }
1108
+ if (!objLiteral) continue;
1109
+ for (const prop of objLiteral.getProperties()) {
1110
+ const propAssign = prop.asKind(SyntaxKind.PropertyAssignment);
1111
+ if (!propAssign) continue;
1112
+ const val = propAssign.getInitializer();
1113
+ const strLiteral = val?.asKind(SyntaxKind.StringLiteral);
1114
+ if (!strLiteral) continue;
1115
+ const value = strLiteral.getLiteralValue();
1116
+ values.push(value);
1117
+ const keyNode = propAssign.getNameNode();
1118
+ if (keyNode.getKind() === SyntaxKind.Identifier) {
1119
+ keyToValue[keyNode.getText()] = value;
1118
1120
  }
1119
1121
  }
1120
1122
  }
@@ -1133,24 +1135,26 @@ function extractConstValues(ast, exportName, sourcePath) {
1133
1135
  sourcePath
1134
1136
  };
1135
1137
  }
1136
- function extractZodKeys(ast, exportName, sourcePath) {
1138
+ function extractZodKeys(sourceFile, exportName, sourcePath) {
1137
1139
  const values = [];
1138
- for (const node of ast.body) {
1139
- if (node.type === "ExportNamedDeclaration" && node.declaration?.type === "VariableDeclaration") {
1140
- for (const declarator of node.declaration.declarations) {
1141
- if (declarator.id.type === "Identifier" && declarator.id.name === exportName && declarator.init) {
1142
- const init = declarator.init;
1143
- if (init.type === "CallExpression" && init.callee.type === "MemberExpression" && init.callee.property.type === "Identifier" && init.callee.property.name === "object") {
1144
- const firstArg = init.arguments[0];
1145
- if (firstArg && firstArg.type === "ObjectExpression") {
1146
- for (const prop of firstArg.properties) {
1147
- if (prop.type === "Property" && prop.key.type === "Identifier") {
1148
- values.push(prop.key.name);
1149
- }
1150
- }
1151
- }
1152
- }
1153
- }
1140
+ for (const varStmt of sourceFile.getVariableStatements()) {
1141
+ if (!varStmt.isExported()) continue;
1142
+ for (const decl of varStmt.getDeclarations()) {
1143
+ if (decl.getName() !== exportName) continue;
1144
+ const init = decl.getInitializer();
1145
+ if (!init) continue;
1146
+ const callExpr = init.asKind(SyntaxKind.CallExpression);
1147
+ if (!callExpr) continue;
1148
+ const callee = callExpr.getExpression();
1149
+ if (!callee.getText().endsWith(".object")) continue;
1150
+ const firstArg = callExpr.getArguments()[0];
1151
+ if (!firstArg) continue;
1152
+ const objLiteral = firstArg.asKind(SyntaxKind.ObjectLiteralExpression);
1153
+ if (!objLiteral) continue;
1154
+ for (const prop of objLiteral.getProperties()) {
1155
+ const propAssign = prop.asKind(SyntaxKind.PropertyAssignment);
1156
+ if (!propAssign) continue;
1157
+ values.push(propAssign.getName());
1154
1158
  }
1155
1159
  }
1156
1160
  }
@@ -1171,7 +1175,7 @@ function extractZodKeys(ast, exportName, sourcePath) {
1171
1175
  // src/lib/contracts/contract-parser.ts
1172
1176
  import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
1173
1177
  import fg7 from "fast-glob";
1174
- import { parse as parse2 } from "@typescript-eslint/typescript-estree";
1178
+ import { Project as Project2, SyntaxKind as SyntaxKind2 } from "ts-morph";
1175
1179
  async function findContractFiles(repoPath) {
1176
1180
  const files = await fg7("**/*.contract.ts", {
1177
1181
  cwd: repoPath,
@@ -1198,12 +1202,13 @@ function parseContractFile(filePath) {
1198
1202
  filePath
1199
1203
  };
1200
1204
  }
1201
- let ast;
1205
+ const project = new Project2({
1206
+ useInMemoryFileSystem: true,
1207
+ skipAddingFilesFromTsConfig: true
1208
+ });
1209
+ let sourceFile;
1202
1210
  try {
1203
- ast = parse2(content, {
1204
- loc: true,
1205
- range: true
1206
- });
1211
+ sourceFile = project.createSourceFile("contract.ts", content);
1207
1212
  } catch (err) {
1208
1213
  return {
1209
1214
  success: false,
@@ -1213,20 +1218,15 @@ function parseContractFile(filePath) {
1213
1218
  }
1214
1219
  const endpoints = [];
1215
1220
  let contractName = "UnknownContract";
1216
- for (const node of ast.body) {
1217
- if (node.type === "ExportNamedDeclaration" && node.declaration?.type === "VariableDeclaration") {
1218
- for (const declarator of node.declaration.declarations) {
1219
- if (declarator.id.type === "Identifier") {
1220
- contractName = declarator.id.name;
1221
- const routerCall = findRouterCall(declarator.init);
1222
- if (routerCall) {
1223
- const parsedEndpoints = extractEndpointsFromRouter(
1224
- routerCall,
1225
- content
1226
- );
1227
- endpoints.push(...parsedEndpoints);
1228
- }
1229
- }
1221
+ for (const varStmt of sourceFile.getVariableStatements()) {
1222
+ if (!varStmt.isExported()) continue;
1223
+ for (const decl of varStmt.getDeclarations()) {
1224
+ contractName = decl.getName();
1225
+ const init = decl.getInitializer();
1226
+ const routerObj = findRouterObjectArg(init);
1227
+ if (routerObj) {
1228
+ const parsedEndpoints = extractEndpointsFromRouter(routerObj);
1229
+ endpoints.push(...parsedEndpoints);
1230
1230
  }
1231
1231
  }
1232
1232
  }
@@ -1265,31 +1265,33 @@ async function parseContracts(repoPath) {
1265
1265
  contracts
1266
1266
  };
1267
1267
  }
1268
- function findRouterCall(node) {
1268
+ function findRouterObjectArg(node) {
1269
1269
  if (!node) return null;
1270
- if (node.type === "CallExpression" && node.callee.type === "MemberExpression" && node.callee.property.type === "Identifier" && node.callee.property.name === "router") {
1271
- return node;
1272
- }
1273
- return null;
1274
- }
1275
- function extractEndpointsFromRouter(routerCall, sourceContent) {
1270
+ const callExpr = node.asKind(SyntaxKind2.CallExpression);
1271
+ if (!callExpr) return null;
1272
+ const callee = callExpr.getExpression();
1273
+ const propAccess = callee.asKind(SyntaxKind2.PropertyAccessExpression);
1274
+ if (!propAccess || propAccess.getName() !== "router") return null;
1275
+ const firstArg = callExpr.getArguments()[0];
1276
+ if (!firstArg) return null;
1277
+ return firstArg.asKind(SyntaxKind2.ObjectLiteralExpression) ?? null;
1278
+ }
1279
+ function extractEndpointsFromRouter(routerObj) {
1276
1280
  const endpoints = [];
1277
- const firstArg = routerCall.arguments[0];
1278
- if (!firstArg || firstArg.type !== "ObjectExpression") {
1279
- return endpoints;
1280
- }
1281
- const routerObject = firstArg;
1282
- for (const prop of routerObject.properties) {
1283
- if (prop.type !== "Property") continue;
1284
- if (prop.key.type !== "Identifier") continue;
1285
- const endpointName = prop.key.name;
1286
- const endpointLine = prop.loc?.start.line ?? 0;
1287
- if (prop.value.type !== "ObjectExpression") continue;
1281
+ for (const prop of routerObj.getProperties()) {
1282
+ const propAssign = prop.asKind(SyntaxKind2.PropertyAssignment);
1283
+ if (!propAssign) continue;
1284
+ const endpointName = propAssign.getName();
1285
+ const endpointLine = propAssign.getStartLineNumber();
1286
+ const endpointValue = propAssign.getInitializer();
1287
+ const endpointObj = endpointValue?.asKind(
1288
+ SyntaxKind2.ObjectLiteralExpression
1289
+ );
1290
+ if (!endpointObj) continue;
1288
1291
  const endpoint = extractEndpointDetails(
1289
1292
  endpointName,
1290
- prop.value,
1291
- endpointLine,
1292
- sourceContent
1293
+ endpointObj,
1294
+ endpointLine
1293
1295
  );
1294
1296
  if (endpoint) {
1295
1297
  endpoints.push(endpoint);
@@ -1297,28 +1299,34 @@ function extractEndpointsFromRouter(routerCall, sourceContent) {
1297
1299
  }
1298
1300
  return endpoints;
1299
1301
  }
1300
- function extractEndpointDetails(name, objectExpr, line, sourceContent) {
1302
+ function extractEndpointDetails(name, endpointObj, line) {
1301
1303
  let method = "";
1302
1304
  let path = "";
1303
1305
  let metadata;
1304
1306
  let strictStatusCodes;
1305
- for (const prop of objectExpr.properties) {
1306
- if (prop.type !== "Property") continue;
1307
- if (prop.key.type !== "Identifier") continue;
1308
- const propName = prop.key.name;
1309
- if (propName === "method" && prop.value.type === "Literal") {
1310
- method = String(prop.value.value);
1311
- }
1312
- if (propName === "path" && prop.value.type === "Literal") {
1313
- path = String(prop.value.value);
1314
- }
1315
- if (propName === "metadata") {
1316
- if (prop.value.type === "ObjectExpression" || prop.value.type === "CallExpression") {
1317
- metadata = extractMetadata(prop.value, sourceContent);
1307
+ for (const prop of endpointObj.getProperties()) {
1308
+ const propAssign = prop.asKind(SyntaxKind2.PropertyAssignment);
1309
+ if (!propAssign) continue;
1310
+ const propName = propAssign.getName();
1311
+ const propVal = propAssign.getInitializer();
1312
+ if (!propVal) continue;
1313
+ if (propName === "method") {
1314
+ const strLit = propVal.asKind(SyntaxKind2.StringLiteral);
1315
+ if (strLit) method = strLit.getLiteralValue();
1316
+ }
1317
+ if (propName === "path") {
1318
+ const strLit = propVal.asKind(SyntaxKind2.StringLiteral);
1319
+ if (strLit) path = strLit.getLiteralValue();
1320
+ }
1321
+ if (propName === "strictStatusCodes") {
1322
+ if (propVal.getKind() === SyntaxKind2.TrueKeyword) {
1323
+ strictStatusCodes = true;
1324
+ } else if (propVal.getKind() === SyntaxKind2.FalseKeyword) {
1325
+ strictStatusCodes = false;
1318
1326
  }
1319
1327
  }
1320
- if (propName === "strictStatusCodes" && prop.value.type === "Literal") {
1321
- strictStatusCodes = prop.value.value === true;
1328
+ if (propName === "metadata") {
1329
+ metadata = extractMetadata(propVal);
1322
1330
  }
1323
1331
  }
1324
1332
  return {
@@ -1330,82 +1338,105 @@ function extractEndpointDetails(name, objectExpr, line, sourceContent) {
1330
1338
  line
1331
1339
  };
1332
1340
  }
1333
- function extractMetadata(node, sourceContent) {
1334
- if (node.type === "ObjectExpression") {
1335
- return parseMetadataObject(node, sourceContent);
1336
- }
1337
- if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "contractMetadata") {
1338
- const firstArg = node.arguments[0];
1339
- if (firstArg && firstArg.type === "ObjectExpression") {
1340
- return parseMetadataObject(firstArg, sourceContent);
1341
+ function extractMetadata(node) {
1342
+ const objLiteral = node.asKind(SyntaxKind2.ObjectLiteralExpression);
1343
+ if (objLiteral) {
1344
+ return parseMetadataObject(objLiteral);
1345
+ }
1346
+ const callExpr = node.asKind(SyntaxKind2.CallExpression);
1347
+ if (callExpr) {
1348
+ const callee = callExpr.getExpression();
1349
+ if (callee.getKind() === SyntaxKind2.Identifier && callee.getText() === "contractMetadata") {
1350
+ const firstArg = callExpr.getArguments()[0];
1351
+ if (firstArg) {
1352
+ const argObj = firstArg.asKind(SyntaxKind2.ObjectLiteralExpression);
1353
+ if (argObj) {
1354
+ return parseMetadataObject(argObj);
1355
+ }
1356
+ }
1341
1357
  }
1342
1358
  }
1343
1359
  return void 0;
1344
1360
  }
1345
- function parseMetadataObject(objectExpr, sourceContent) {
1361
+ function parseMetadataObject(objLiteral) {
1346
1362
  const metadata = {};
1347
- for (const prop of objectExpr.properties) {
1348
- if (prop.type !== "Property") continue;
1349
- if (prop.key.type !== "Identifier") continue;
1350
- const propName = prop.key.name;
1351
- if (propName === "openApiTags" && prop.value.type === "ArrayExpression") {
1352
- metadata.openApiTags = extractStringArray(prop.value);
1363
+ for (const prop of objLiteral.getProperties()) {
1364
+ const propAssign = prop.asKind(SyntaxKind2.PropertyAssignment);
1365
+ if (!propAssign) continue;
1366
+ const propName = propAssign.getName();
1367
+ const propVal = propAssign.getInitializer();
1368
+ if (!propVal) continue;
1369
+ if (propName === "openApiTags") {
1370
+ const arr = propVal.asKind(SyntaxKind2.ArrayLiteralExpression);
1371
+ if (arr) {
1372
+ metadata.openApiTags = extractStringArray(arr);
1373
+ }
1353
1374
  }
1354
1375
  if (propName === "requiredPermission") {
1355
- if (prop.value.type === "Literal" || prop.value.type === "MemberExpression") {
1356
- const permValue = extractPermissionValue(prop.value, sourceContent);
1357
- if (permValue) {
1358
- metadata.requiredPermission = permValue;
1359
- }
1376
+ const permValue = extractPermissionValue(propVal);
1377
+ if (permValue) {
1378
+ metadata.requiredPermission = permValue;
1360
1379
  }
1361
1380
  }
1362
- if (propName === "requiresAuth" && prop.value.type === "Literal") {
1363
- metadata.requiresAuth = prop.value.value === true;
1381
+ if (propName === "requiresAuth") {
1382
+ metadata.requiresAuth = propVal.getKind() === SyntaxKind2.TrueKeyword;
1364
1383
  }
1365
- if (propName === "bff" && prop.value.type === "ObjectExpression") {
1366
- metadata.bff = extractBffConfig(prop.value);
1384
+ if (propName === "bff") {
1385
+ const bffObj = propVal.asKind(SyntaxKind2.ObjectLiteralExpression);
1386
+ if (bffObj) {
1387
+ metadata.bff = extractBffConfig(bffObj);
1388
+ }
1367
1389
  }
1368
- if (propName === "openapi" && prop.value.type === "ObjectExpression") {
1369
- metadata.openapi = extractLegacyOpenApi(prop.value);
1390
+ if (propName === "openapi") {
1391
+ const openapiObj = propVal.asKind(SyntaxKind2.ObjectLiteralExpression);
1392
+ if (openapiObj) {
1393
+ metadata.openapi = extractLegacyOpenApi(openapiObj);
1394
+ }
1370
1395
  }
1371
1396
  }
1372
1397
  return metadata;
1373
1398
  }
1374
1399
  function extractStringArray(arrayExpr) {
1375
1400
  const values = [];
1376
- for (const element of arrayExpr.elements) {
1377
- if (element?.type === "Literal" && typeof element.value === "string") {
1378
- values.push(element.value);
1401
+ for (const element of arrayExpr.getElements()) {
1402
+ const strLit = element.asKind(SyntaxKind2.StringLiteral);
1403
+ if (strLit) {
1404
+ values.push(strLit.getLiteralValue());
1379
1405
  }
1380
1406
  }
1381
1407
  return values;
1382
1408
  }
1383
- function extractPermissionValue(node, sourceContent) {
1384
- if (node.type === "Literal" && typeof node.value === "string") {
1385
- return node.value;
1409
+ function extractPermissionValue(node) {
1410
+ const strLit = node.asKind(SyntaxKind2.StringLiteral);
1411
+ if (strLit) {
1412
+ return strLit.getLiteralValue();
1386
1413
  }
1387
- if (node.type === "MemberExpression") {
1388
- if (node.range) {
1389
- const [start, end] = node.range;
1390
- return sourceContent.slice(start, end);
1391
- }
1414
+ const propAccess = node.asKind(SyntaxKind2.PropertyAccessExpression);
1415
+ if (propAccess) {
1416
+ return propAccess.getText();
1392
1417
  }
1393
1418
  return void 0;
1394
1419
  }
1395
- function extractBffConfig(objectExpr) {
1420
+ function extractBffConfig(objLiteral) {
1396
1421
  const bff = {};
1397
- for (const prop of objectExpr.properties) {
1398
- if (prop.type !== "Property") continue;
1399
- if (prop.key.type !== "Identifier") continue;
1400
- if (prop.key.name === "apiServices" && prop.value.type === "ArrayExpression") {
1401
- bff.apiServices = [];
1402
- for (const element of prop.value.elements) {
1403
- if (element?.type === "Literal" && typeof element.value === "string") {
1404
- bff.apiServices.push(element.value);
1405
- }
1406
- if (element?.type === "MemberExpression") {
1407
- if (element.property.type === "Identifier") {
1408
- bff.apiServices.push(element.property.name);
1422
+ for (const prop of objLiteral.getProperties()) {
1423
+ const propAssign = prop.asKind(SyntaxKind2.PropertyAssignment);
1424
+ if (!propAssign) continue;
1425
+ if (propAssign.getName() === "apiServices") {
1426
+ const arr = propAssign.getInitializer()?.asKind(SyntaxKind2.ArrayLiteralExpression);
1427
+ if (arr) {
1428
+ bff.apiServices = [];
1429
+ for (const element of arr.getElements()) {
1430
+ const strLit = element.asKind(SyntaxKind2.StringLiteral);
1431
+ if (strLit) {
1432
+ bff.apiServices.push(strLit.getLiteralValue());
1433
+ continue;
1434
+ }
1435
+ const propAccess = element.asKind(
1436
+ SyntaxKind2.PropertyAccessExpression
1437
+ );
1438
+ if (propAccess) {
1439
+ bff.apiServices.push(propAccess.getName());
1409
1440
  }
1410
1441
  }
1411
1442
  }
@@ -1413,13 +1444,16 @@ function extractBffConfig(objectExpr) {
1413
1444
  }
1414
1445
  return bff;
1415
1446
  }
1416
- function extractLegacyOpenApi(objectExpr) {
1447
+ function extractLegacyOpenApi(objLiteral) {
1417
1448
  const openapi = {};
1418
- for (const prop of objectExpr.properties) {
1419
- if (prop.type !== "Property") continue;
1420
- if (prop.key.type !== "Identifier") continue;
1421
- if (prop.key.name === "tags" && prop.value.type === "ArrayExpression") {
1422
- openapi.tags = extractStringArray(prop.value);
1449
+ for (const prop of objLiteral.getProperties()) {
1450
+ const propAssign = prop.asKind(SyntaxKind2.PropertyAssignment);
1451
+ if (!propAssign) continue;
1452
+ if (propAssign.getName() === "tags") {
1453
+ const arr = propAssign.getInitializer()?.asKind(SyntaxKind2.ArrayLiteralExpression);
1454
+ if (arr) {
1455
+ openapi.tags = extractStringArray(arr);
1456
+ }
1423
1457
  }
1424
1458
  }
1425
1459
  return openapi;
@@ -2392,7 +2426,7 @@ function checkSchemaImport(contractFile, _contractDir, imp) {
2392
2426
  import fg9 from "fast-glob";
2393
2427
  import { readFileSync as readFileSync4 } from "fs";
2394
2428
  import { basename as basename5 } from "path";
2395
- import { parse as parse3 } from "@typescript-eslint/typescript-estree";
2429
+ import { Node, Project as Project3, SyntaxKind as SyntaxKind3 } from "ts-morph";
2396
2430
  var ALLOWED_SUFFIXES = [
2397
2431
  ".enum-bundle.ts",
2398
2432
  ".schema.ts",
@@ -2409,15 +2443,16 @@ var ALLOWED_SUFFIXES = [
2409
2443
  ".rules.ts"
2410
2444
  ];
2411
2445
  var ALLOWED_SPECIAL_FILES = ["index.ts", "openapi.ts"];
2412
- function parseFile(filePath) {
2446
+ function createProject() {
2447
+ return new Project3({
2448
+ useInMemoryFileSystem: true,
2449
+ skipAddingFilesFromTsConfig: true
2450
+ });
2451
+ }
2452
+ function parseFile(filePath, project) {
2413
2453
  try {
2414
2454
  const content = readFileSync4(filePath, "utf-8");
2415
- return parse3(content, {
2416
- loc: true,
2417
- range: true,
2418
- comment: true,
2419
- jsx: false
2420
- });
2455
+ return project.createSourceFile(filePath, content, { overwrite: true });
2421
2456
  } catch {
2422
2457
  return null;
2423
2458
  }
@@ -2425,48 +2460,58 @@ function parseFile(filePath) {
2425
2460
  function readFileContent(filePath) {
2426
2461
  return readFileSync4(filePath, "utf-8");
2427
2462
  }
2428
- function getExportedNames(ast) {
2463
+ function getExportedNames(sourceFile) {
2429
2464
  const names = [];
2430
- for (const node of ast.body) {
2431
- if (node.type === "ExportNamedDeclaration") {
2432
- if (node.declaration) {
2433
- if (node.declaration.type === "VariableDeclaration") {
2434
- for (const decl of node.declaration.declarations) {
2435
- if (decl.id.type === "Identifier") {
2436
- names.push(decl.id.name);
2437
- } else if (decl.id.type === "ObjectPattern") {
2438
- for (const prop of decl.id.properties) {
2439
- if (prop.type === "Property" && prop.value.type === "Identifier") {
2440
- names.push(prop.value.name);
2441
- }
2442
- }
2465
+ for (const statement of sourceFile.getStatements()) {
2466
+ const kind = statement.getKind();
2467
+ if (kind === SyntaxKind3.ExportDeclaration) {
2468
+ const exportDecl = statement.asKindOrThrow(SyntaxKind3.ExportDeclaration);
2469
+ for (const namedExport of exportDecl.getNamedExports()) {
2470
+ names.push(
2471
+ namedExport.getAliasNode()?.getText() ?? namedExport.getName()
2472
+ );
2473
+ }
2474
+ }
2475
+ if (Node.isModifierable(statement) && statement.hasModifier(SyntaxKind3.ExportKeyword)) {
2476
+ if (kind === SyntaxKind3.VariableStatement) {
2477
+ const varStatement = statement.asKindOrThrow(
2478
+ SyntaxKind3.VariableStatement
2479
+ );
2480
+ for (const decl of varStatement.getDeclarations()) {
2481
+ const nameNode = decl.getNameNode();
2482
+ if (nameNode.getKind() === SyntaxKind3.Identifier) {
2483
+ names.push(nameNode.getText());
2484
+ } else if (nameNode.getKind() === SyntaxKind3.ObjectBindingPattern) {
2485
+ const objectPattern = nameNode.asKindOrThrow(
2486
+ SyntaxKind3.ObjectBindingPattern
2487
+ );
2488
+ for (const element of objectPattern.getElements()) {
2489
+ const propertyName = element.getPropertyNameNode();
2490
+ const elementName = element.getNameNode();
2491
+ names.push(elementName.getText());
2443
2492
  }
2444
2493
  }
2445
2494
  }
2446
- if (node.declaration.type === "TSTypeAliasDeclaration") {
2447
- names.push(node.declaration.id.name);
2448
- }
2449
- if (node.declaration.type === "FunctionDeclaration" && node.declaration.id) {
2450
- names.push(node.declaration.id.name);
2451
- }
2452
2495
  }
2453
- if (node.specifiers) {
2454
- for (const spec of node.specifiers) {
2455
- if (spec.exported.type === "Identifier") {
2456
- names.push(spec.exported.name);
2457
- }
2458
- }
2496
+ if (kind === SyntaxKind3.TypeAliasDeclaration) {
2497
+ const typeAlias = statement.asKindOrThrow(
2498
+ SyntaxKind3.TypeAliasDeclaration
2499
+ );
2500
+ names.push(typeAlias.getName());
2501
+ }
2502
+ if (kind === SyntaxKind3.FunctionDeclaration) {
2503
+ const funcDecl = statement.asKindOrThrow(
2504
+ SyntaxKind3.FunctionDeclaration
2505
+ );
2506
+ const funcName = funcDecl.getName();
2507
+ if (funcName) names.push(funcName);
2459
2508
  }
2460
2509
  }
2461
2510
  }
2462
2511
  return names;
2463
2512
  }
2464
- function hasDefaultExport(ast) {
2465
- return ast.body.some(
2466
- (node) => node.type === "ExportDefaultDeclaration" || node.type === "ExportNamedDeclaration" && node.specifiers?.some(
2467
- (s) => s.exported.type === "Identifier" && s.exported.name === "default"
2468
- )
2469
- );
2513
+ function hasDefaultExport(sourceFile) {
2514
+ return sourceFile.getExportedDeclarations().has("default");
2470
2515
  }
2471
2516
  function hasOpenApiDocumentation(content) {
2472
2517
  return /\.describe\s*\(/.test(content) || /\.openapi\s*\(/.test(content);
@@ -2482,47 +2527,66 @@ function hasZodDataSchemas(content) {
2482
2527
  content
2483
2528
  );
2484
2529
  }
2485
- function getExportsWithoutDeprecatedJsdoc(ast, content) {
2530
+ function getExportsWithoutDeprecatedJsdoc(sourceFile) {
2486
2531
  const missingDeprecated = [];
2487
- for (const node of ast.body) {
2488
- if (node.type !== "ExportNamedDeclaration" && node.type !== "ExportDefaultDeclaration") {
2532
+ for (const statement of sourceFile.getStatements()) {
2533
+ const kind = statement.getKind();
2534
+ const isExported = Node.isModifierable(statement) && statement.hasModifier(SyntaxKind3.ExportKeyword);
2535
+ if (kind !== SyntaxKind3.ExportDeclaration && kind !== SyntaxKind3.ExportAssignment && !isExported) {
2489
2536
  continue;
2490
2537
  }
2491
- let exportName = "default";
2492
- if (node.type === "ExportNamedDeclaration") {
2493
- if (node.declaration) {
2494
- if (node.declaration.type === "VariableDeclaration") {
2495
- const decl = node.declaration.declarations[0];
2496
- if (decl?.id.type === "Identifier") {
2497
- exportName = decl.id.name;
2498
- }
2499
- } else if (node.declaration.type === "TSTypeAliasDeclaration" || node.declaration.type === "FunctionDeclaration" && node.declaration.id) {
2500
- exportName = node.declaration.type === "TSTypeAliasDeclaration" ? node.declaration.id.name : node.declaration.id.name;
2501
- }
2538
+ if (kind === SyntaxKind3.ExportAssignment) {
2539
+ if (!hasDeprecatedComment(statement)) {
2540
+ missingDeprecated.push("default");
2502
2541
  }
2503
- if (node.source) continue;
2542
+ continue;
2543
+ }
2544
+ if (kind === SyntaxKind3.ExportDeclaration) {
2545
+ const exportDecl = statement.asKindOrThrow(SyntaxKind3.ExportDeclaration);
2546
+ if (exportDecl.getModuleSpecifier() !== void 0) continue;
2504
2547
  }
2505
- const linesBefore = content.substring(0, node.range?.[0] ?? 0);
2506
- const lastCommentBlock = linesBefore.match(/\/\*\*[\s\S]*?\*\/\s*$/);
2507
- if (!lastCommentBlock || !lastCommentBlock[0].includes("@deprecated")) {
2548
+ let exportName = "unknown";
2549
+ if (kind === SyntaxKind3.VariableStatement) {
2550
+ const varStatement = statement.asKindOrThrow(
2551
+ SyntaxKind3.VariableStatement
2552
+ );
2553
+ const declarations = varStatement.getDeclarations();
2554
+ const firstDecl = declarations[0];
2555
+ if (firstDecl) {
2556
+ exportName = firstDecl.getName();
2557
+ }
2558
+ } else if (kind === SyntaxKind3.TypeAliasDeclaration) {
2559
+ const typeAlias = statement.asKindOrThrow(
2560
+ SyntaxKind3.TypeAliasDeclaration
2561
+ );
2562
+ exportName = typeAlias.getName();
2563
+ } else if (kind === SyntaxKind3.FunctionDeclaration) {
2564
+ const funcDecl = statement.asKindOrThrow(SyntaxKind3.FunctionDeclaration);
2565
+ exportName = funcDecl.getName() ?? "anonymous";
2566
+ }
2567
+ if (!hasDeprecatedComment(statement)) {
2508
2568
  missingDeprecated.push(exportName);
2509
2569
  }
2510
2570
  }
2511
2571
  return missingDeprecated;
2512
2572
  }
2513
- function getNonReexportStatements(ast) {
2573
+ function hasDeprecatedComment(node) {
2574
+ return node.getLeadingCommentRanges().some((comment) => comment.getText().includes("@deprecated"));
2575
+ }
2576
+ function getNonReexportStatements(sourceFile) {
2514
2577
  const violations = [];
2515
- for (const node of ast.body) {
2516
- if (node.type === "ImportDeclaration") continue;
2517
- if (node.type === "ExportAllDeclaration") continue;
2518
- if (node.type === "ExportNamedDeclaration" && node.source) continue;
2519
- if (node.type === "ExportNamedDeclaration" && node.exportKind === "type" && node.source)
2520
- continue;
2521
- violations.push(node);
2578
+ for (const statement of sourceFile.getStatements()) {
2579
+ const kind = statement.getKind();
2580
+ if (kind === SyntaxKind3.ImportDeclaration) continue;
2581
+ if (kind === SyntaxKind3.ExportDeclaration) {
2582
+ const exportDecl = statement.asKindOrThrow(SyntaxKind3.ExportDeclaration);
2583
+ if (exportDecl.getModuleSpecifier() !== void 0) continue;
2584
+ }
2585
+ violations.push(statement);
2522
2586
  }
2523
2587
  return violations;
2524
2588
  }
2525
- async function checkEnumBundleRules(cwd) {
2589
+ async function checkEnumBundleRules(cwd, project) {
2526
2590
  const results = [];
2527
2591
  const files = await fg9("src/**/*.enum-bundle.ts", {
2528
2592
  cwd,
@@ -2531,9 +2595,9 @@ async function checkEnumBundleRules(cwd) {
2531
2595
  for (const file of files) {
2532
2596
  const fullPath = `${cwd}/${file}`;
2533
2597
  const content = readFileContent(fullPath);
2534
- const ast = parseFile(fullPath);
2535
- if (!ast) continue;
2536
- const exportedNames = getExportedNames(ast);
2598
+ const sourceFile = parseFile(fullPath, project);
2599
+ if (!sourceFile) continue;
2600
+ const exportedNames = getExportedNames(sourceFile);
2537
2601
  const fileName = basename5(file, ".enum-bundle.ts");
2538
2602
  const upperName = fileName.replace(/[^a-zA-Z0-9]/g, "_").toUpperCase();
2539
2603
  const hasSchema = exportedNames.some((n) => n.endsWith("Schema"));
@@ -2575,7 +2639,7 @@ async function checkEnumBundleRules(cwd) {
2575
2639
  }
2576
2640
  return results;
2577
2641
  }
2578
- async function checkResponseRules(cwd) {
2642
+ async function checkResponseRules(cwd, project) {
2579
2643
  const results = [];
2580
2644
  const files = await fg9("src/**/*.response.ts", {
2581
2645
  cwd,
@@ -2584,9 +2648,9 @@ async function checkResponseRules(cwd) {
2584
2648
  for (const file of files) {
2585
2649
  const fullPath = `${cwd}/${file}`;
2586
2650
  const content = readFileContent(fullPath);
2587
- const ast = parseFile(fullPath);
2588
- if (!ast) continue;
2589
- const exportedNames = getExportedNames(ast);
2651
+ const sourceFile = parseFile(fullPath, project);
2652
+ if (!sourceFile) continue;
2653
+ const exportedNames = getExportedNames(sourceFile);
2590
2654
  const fileName = basename5(file, ".response.ts");
2591
2655
  const httpCode = fileName.match(/^(\d{3})$/)?.[1];
2592
2656
  if (httpCode) {
@@ -2600,7 +2664,7 @@ async function checkResponseRules(cwd) {
2600
2664
  });
2601
2665
  }
2602
2666
  }
2603
- if (!hasDefaultExport(ast)) {
2667
+ if (!hasDefaultExport(sourceFile)) {
2604
2668
  results.push({
2605
2669
  ruleId: "CTR-RESPONSE-002",
2606
2670
  severity: "error",
@@ -2621,7 +2685,7 @@ async function checkResponseRules(cwd) {
2621
2685
  }
2622
2686
  return results;
2623
2687
  }
2624
- async function checkRequestRules(cwd) {
2688
+ async function checkRequestRules(cwd, project) {
2625
2689
  const results = [];
2626
2690
  const files = await fg9("src/**/*.request.ts", {
2627
2691
  cwd,
@@ -2630,9 +2694,9 @@ async function checkRequestRules(cwd) {
2630
2694
  for (const file of files) {
2631
2695
  const fullPath = `${cwd}/${file}`;
2632
2696
  const content = readFileContent(fullPath);
2633
- const ast = parseFile(fullPath);
2634
- if (!ast) continue;
2635
- const exportedNames = getExportedNames(ast);
2697
+ const sourceFile = parseFile(fullPath, project);
2698
+ if (!sourceFile) continue;
2699
+ const exportedNames = getExportedNames(sourceFile);
2636
2700
  const hasTypedSchema = exportedNames.some(
2637
2701
  (n) => /(Query|Body|Headers|Params)Schema$/.test(n)
2638
2702
  );
@@ -2657,7 +2721,7 @@ async function checkRequestRules(cwd) {
2657
2721
  }
2658
2722
  return results;
2659
2723
  }
2660
- async function checkContractFileRules(cwd) {
2724
+ async function checkContractFileRules(cwd, project) {
2661
2725
  const results = [];
2662
2726
  const files = await fg9("src/**/*.contract.ts", {
2663
2727
  cwd,
@@ -2666,9 +2730,9 @@ async function checkContractFileRules(cwd) {
2666
2730
  for (const file of files) {
2667
2731
  const fullPath = `${cwd}/${file}`;
2668
2732
  const content = readFileContent(fullPath);
2669
- const ast = parseFile(fullPath);
2670
- if (!ast) continue;
2671
- const exportedNames = getExportedNames(ast);
2733
+ const sourceFile = parseFile(fullPath, project);
2734
+ if (!sourceFile) continue;
2735
+ const exportedNames = getExportedNames(sourceFile);
2672
2736
  const hasContractExport = exportedNames.some((n) => n.endsWith("Contract"));
2673
2737
  if (!hasContractExport) {
2674
2738
  results.push({
@@ -2700,7 +2764,7 @@ async function checkContractFileRules(cwd) {
2700
2764
  }
2701
2765
  return results;
2702
2766
  }
2703
- async function checkIndexRules(cwd) {
2767
+ async function checkIndexRules(cwd, project) {
2704
2768
  const results = [];
2705
2769
  const files = await fg9("src/**/index.ts", {
2706
2770
  cwd,
@@ -2708,16 +2772,16 @@ async function checkIndexRules(cwd) {
2708
2772
  });
2709
2773
  for (const file of files) {
2710
2774
  const fullPath = `${cwd}/${file}`;
2711
- const ast = parseFile(fullPath);
2712
- if (!ast) continue;
2713
- const violations = getNonReexportStatements(ast);
2775
+ const sourceFile = parseFile(fullPath, project);
2776
+ if (!sourceFile) continue;
2777
+ const violations = getNonReexportStatements(sourceFile);
2714
2778
  if (violations.length > 0) {
2715
2779
  results.push({
2716
2780
  ruleId: "CTR-INDEX-001",
2717
2781
  severity: "error",
2718
2782
  message: `Index file ${file} contains ${violations.length} non-reexport statement(s)`,
2719
2783
  file,
2720
- line: violations[0]?.loc?.start.line,
2784
+ line: violations[0]?.getStartLineNumber(),
2721
2785
  suggestion: "Index files should only contain: export * from '...' or export { X } from '...'"
2722
2786
  });
2723
2787
  }
@@ -2774,7 +2838,7 @@ async function checkNamingRules(cwd) {
2774
2838
  }
2775
2839
  return results;
2776
2840
  }
2777
- async function checkDeprecatedRules(cwd) {
2841
+ async function checkDeprecatedRules(cwd, project) {
2778
2842
  const results = [];
2779
2843
  const files = await fg9("src/**/*.deprecated.ts", {
2780
2844
  cwd,
@@ -2782,10 +2846,9 @@ async function checkDeprecatedRules(cwd) {
2782
2846
  });
2783
2847
  for (const file of files) {
2784
2848
  const fullPath = `${cwd}/${file}`;
2785
- const content = readFileContent(fullPath);
2786
- const ast = parseFile(fullPath);
2787
- if (!ast) continue;
2788
- const missing = getExportsWithoutDeprecatedJsdoc(ast, content);
2849
+ const sourceFile = parseFile(fullPath, project);
2850
+ if (!sourceFile) continue;
2851
+ const missing = getExportsWithoutDeprecatedJsdoc(sourceFile);
2789
2852
  if (missing.length > 0) {
2790
2853
  results.push({
2791
2854
  ruleId: "CTR-DEPRECATED-001",
@@ -2824,6 +2887,7 @@ var contractsNomenclatureChecker = {
2824
2887
  }
2825
2888
  const disabledRules = getDisabledRules(configResult.config);
2826
2889
  const results = [];
2890
+ const project = createProject();
2827
2891
  const [
2828
2892
  enumResults,
2829
2893
  responseResults,
@@ -2834,14 +2898,14 @@ var contractsNomenclatureChecker = {
2834
2898
  namingResults,
2835
2899
  deprecatedResults
2836
2900
  ] = await Promise.all([
2837
- checkEnumBundleRules(ctx.cwd),
2838
- checkResponseRules(ctx.cwd),
2839
- checkRequestRules(ctx.cwd),
2840
- checkContractFileRules(ctx.cwd),
2841
- checkIndexRules(ctx.cwd),
2901
+ checkEnumBundleRules(ctx.cwd, project),
2902
+ checkResponseRules(ctx.cwd, project),
2903
+ checkRequestRules(ctx.cwd, project),
2904
+ checkContractFileRules(ctx.cwd, project),
2905
+ checkIndexRules(ctx.cwd, project),
2842
2906
  checkHelperRules(ctx.cwd),
2843
2907
  checkNamingRules(ctx.cwd),
2844
- checkDeprecatedRules(ctx.cwd)
2908
+ checkDeprecatedRules(ctx.cwd, project)
2845
2909
  ]);
2846
2910
  results.push(
2847
2911
  ...enumResults,
@@ -2948,7 +3012,7 @@ var contractsChecker = {
2948
3012
  // src/lint/checkers/vault/index.ts
2949
3013
  import { existsSync as existsSync5, readdirSync } from "fs";
2950
3014
  import { join as join3, dirname as dirname4 } from "path";
2951
- import { Project, SyntaxKind } from "ts-morph";
3015
+ import { Project as Project4, SyntaxKind as SyntaxKind4 } from "ts-morph";
2952
3016
  var VAULT_PATHS = [
2953
3017
  "src/common/config/vault",
2954
3018
  "server/vault",
@@ -2977,7 +3041,7 @@ function isInsideZodObject(node) {
2977
3041
  if (!node) return false;
2978
3042
  let current = node;
2979
3043
  while (current) {
2980
- if (current.getKind() === SyntaxKind.CallExpression) {
3044
+ if (current.getKind() === SyntaxKind4.CallExpression) {
2981
3045
  const text = current.getText();
2982
3046
  if (text.startsWith("z.object(")) return true;
2983
3047
  }
@@ -2989,7 +3053,7 @@ function checkAstPatterns(sourceFile) {
2989
3053
  const results = [];
2990
3054
  const filePath = sourceFile.getFilePath();
2991
3055
  sourceFile.forEachDescendant((node) => {
2992
- if (node.getKind() === SyntaxKind.CallExpression) {
3056
+ if (node.getKind() === SyntaxKind4.CallExpression) {
2993
3057
  const text = node.getText();
2994
3058
  if (text.startsWith("z.lazy(")) {
2995
3059
  results.push({
@@ -3001,11 +3065,11 @@ function checkAstPatterns(sourceFile) {
3001
3065
  });
3002
3066
  }
3003
3067
  if (text.startsWith("z.enum(")) {
3004
- const callExpr = node.asKind(SyntaxKind.CallExpression);
3068
+ const callExpr = node.asKind(SyntaxKind4.CallExpression);
3005
3069
  const args = callExpr?.getArguments();
3006
3070
  if (args && args.length > 0) {
3007
3071
  const firstArg = args[0];
3008
- if (firstArg?.getKind() === SyntaxKind.Identifier) {
3072
+ if (firstArg?.getKind() === SyntaxKind4.Identifier) {
3009
3073
  results.push({
3010
3074
  ruleId: "VAULT-AST-004",
3011
3075
  severity: getRuleSeverity("VAULT-AST-004"),
@@ -3018,11 +3082,11 @@ function checkAstPatterns(sourceFile) {
3018
3082
  }
3019
3083
  }
3020
3084
  if (text.startsWith("z.object(")) {
3021
- const callExpr = node.asKind(SyntaxKind.CallExpression);
3085
+ const callExpr = node.asKind(SyntaxKind4.CallExpression);
3022
3086
  const args = callExpr?.getArguments();
3023
3087
  if (args && args.length > 0) {
3024
3088
  const firstArg = args[0];
3025
- if (firstArg?.getKind() === SyntaxKind.Identifier) {
3089
+ if (firstArg?.getKind() === SyntaxKind4.Identifier) {
3026
3090
  results.push({
3027
3091
  ruleId: "VAULT-AST-006",
3028
3092
  severity: getRuleSeverity("VAULT-AST-006"),
@@ -3034,11 +3098,11 @@ function checkAstPatterns(sourceFile) {
3034
3098
  }
3035
3099
  }
3036
3100
  }
3037
- if (node.getKind() === SyntaxKind.SpreadAssignment) {
3101
+ if (node.getKind() === SyntaxKind4.SpreadAssignment) {
3038
3102
  if (isInsideZodObject(node)) {
3039
- const spreadExpr = node.asKind(SyntaxKind.SpreadAssignment);
3103
+ const spreadExpr = node.asKind(SyntaxKind4.SpreadAssignment);
3040
3104
  const expr = spreadExpr?.getExpression();
3041
- if (expr?.getKind() === SyntaxKind.ConditionalExpression || expr?.getKind() === SyntaxKind.Identifier) {
3105
+ if (expr?.getKind() === SyntaxKind4.ConditionalExpression || expr?.getKind() === SyntaxKind4.Identifier) {
3042
3106
  results.push({
3043
3107
  ruleId: "VAULT-AST-002",
3044
3108
  severity: getRuleSeverity("VAULT-AST-002"),
@@ -3049,8 +3113,8 @@ function checkAstPatterns(sourceFile) {
3049
3113
  }
3050
3114
  }
3051
3115
  }
3052
- if (node.getKind() === SyntaxKind.FunctionDeclaration || node.getKind() === SyntaxKind.ArrowFunction) {
3053
- const funcNode = node.asKind(SyntaxKind.FunctionDeclaration) || node.asKind(SyntaxKind.ArrowFunction);
3116
+ if (node.getKind() === SyntaxKind4.FunctionDeclaration || node.getKind() === SyntaxKind4.ArrowFunction) {
3117
+ const funcNode = node.asKind(SyntaxKind4.FunctionDeclaration) || node.asKind(SyntaxKind4.ArrowFunction);
3054
3118
  if (funcNode) {
3055
3119
  const body = funcNode.getBody();
3056
3120
  if (body) {
@@ -3082,7 +3146,7 @@ var vaultChecker = {
3082
3146
  }
3083
3147
  const configResult = loadConfig(ctx.cwd);
3084
3148
  const disabledRules = configResult.success ? getDisabledVaultRules(configResult.config) : [];
3085
- const project = new Project({
3149
+ const project = new Project4({
3086
3150
  skipAddingFilesFromTsConfig: true,
3087
3151
  skipFileDependencyResolution: true
3088
3152
  });
package/dist/cli.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  lintCommand,
6
6
  loadConfig,
7
7
  scaffoldCommand
8
- } from "./chunk-EB6S5X3P.js";
8
+ } from "./chunk-73G2DONK.js";
9
9
  import {
10
10
  scanForVaultRepos
11
11
  } from "./chunk-WXFSGE4N.js";
@@ -21,7 +21,7 @@ import { resolve } from "path";
21
21
  // src/lib/contracts/codemod/analyze-bff.ts
22
22
  import { readFileSync } from "fs";
23
23
  import fg from "fast-glob";
24
- import { parse } from "@typescript-eslint/typescript-estree";
24
+ import { Project, SyntaxKind } from "ts-morph";
25
25
  var MODULE_TO_SERVICE = {
26
26
  SeeCoreModule: "seeCore",
27
27
  LocusModule: "locus",
@@ -104,63 +104,48 @@ function extractApiServicesFromModule(modulePath) {
104
104
  return services;
105
105
  }
106
106
  function analyzeController(controllerPath, apiServices) {
107
- const content = readFileSync(controllerPath, "utf-8");
108
107
  const mappings = [];
109
- let ast;
108
+ const project = new Project({ skipAddingFilesFromTsConfig: true });
109
+ let sourceFile;
110
110
  try {
111
- ast = parse(content, { loc: true, range: true, comment: true });
111
+ sourceFile = project.addSourceFileAtPath(controllerPath);
112
112
  } catch {
113
113
  return mappings;
114
114
  }
115
- for (const node of ast.body) {
116
- if (node.type === "ExportNamedDeclaration" && node.declaration?.type === "ClassDeclaration") {
117
- const classNode = node.declaration;
118
- analyzeClassMethods(
119
- classNode,
120
- controllerPath,
121
- apiServices,
122
- content,
123
- mappings
124
- );
125
- }
126
- if (node.type === "ClassDeclaration") {
127
- analyzeClassMethods(node, controllerPath, apiServices, content, mappings);
128
- }
115
+ for (const classDecl of sourceFile.getClasses()) {
116
+ analyzeClassMethods(classDecl, controllerPath, apiServices, mappings);
129
117
  }
130
118
  return mappings;
131
119
  }
132
- function analyzeClassMethods(classNode, controllerPath, apiServices, content, mappings) {
133
- for (const member of classNode.body.body) {
134
- if (member.type !== "MethodDefinition") continue;
135
- if (member.key.type !== "Identifier") continue;
136
- const methodName = member.key.name;
120
+ function analyzeClassMethods(classDecl, controllerPath, apiServices, mappings) {
121
+ for (const method of classDecl.getMethods()) {
122
+ const methodName = method.getName();
137
123
  if (methodName === "constructor" || methodName.startsWith("_")) continue;
138
- const decorators = member.decorators ?? [];
139
124
  let permission = null;
140
- let method = null;
125
+ let httpMethod = null;
141
126
  let path = null;
142
127
  let contractEndpointName = null;
143
- for (const decorator of decorators) {
144
- if (decorator.expression.type === "CallExpression") {
145
- const callee = decorator.expression.callee;
146
- if (callee.type === "Identifier" && callee.name === "TsRestHandler") {
147
- const arg = decorator.expression.arguments[0];
148
- if (arg?.type === "MemberExpression" && arg.property.type === "Identifier") {
149
- contractEndpointName = arg.property.name;
150
- }
128
+ for (const decorator of method.getDecorators()) {
129
+ const decoratorName = decorator.getName();
130
+ const args = decorator.getArguments();
131
+ if (decoratorName === "TsRestHandler" && args[0]) {
132
+ const arg = args[0];
133
+ if (arg.getKind() === SyntaxKind.PropertyAccessExpression) {
134
+ const propAccess = arg.asKind(SyntaxKind.PropertyAccessExpression);
135
+ contractEndpointName = propAccess.getName();
151
136
  }
152
- if (callee.type === "Identifier" && callee.name === "Authorize") {
153
- const arg = decorator.expression.arguments[0];
154
- if (arg?.type === "MemberExpression" && arg.property.type === "Identifier") {
155
- permission = arg.property.name;
156
- }
137
+ }
138
+ if (decoratorName === "Authorize" && args[0]) {
139
+ const arg = args[0];
140
+ if (arg.getKind() === SyntaxKind.PropertyAccessExpression) {
141
+ const propAccess = arg.asKind(SyntaxKind.PropertyAccessExpression);
142
+ permission = propAccess.getName();
157
143
  }
158
- if (callee.type === "Identifier" && ["Get", "Post", "Put", "Patch", "Delete"].includes(callee.name)) {
159
- method = callee.name.toUpperCase();
160
- const arg = decorator.expression.arguments[0];
161
- if (arg?.type === "Literal" && typeof arg.value === "string") {
162
- path = arg.value;
163
- }
144
+ }
145
+ if (["Get", "Post", "Put", "Patch", "Delete"].includes(decoratorName)) {
146
+ httpMethod = decoratorName.toUpperCase();
147
+ if (args[0]?.getKind() === SyntaxKind.StringLiteral) {
148
+ path = args[0].asKind(SyntaxKind.StringLiteral).getLiteralValue();
164
149
  }
165
150
  }
166
151
  }
@@ -174,7 +159,7 @@ function analyzeClassMethods(classNode, controllerPath, apiServices, content, ma
174
159
  permissionValue,
175
160
  apiModules,
176
161
  apiServices,
177
- method,
162
+ method: httpMethod,
178
163
  path
179
164
  });
180
165
  }
@@ -3,7 +3,7 @@ import {
3
3
  analyzeCore,
4
4
  lintCore,
5
5
  scaffoldCore
6
- } from "./chunk-EB6S5X3P.js";
6
+ } from "./chunk-73G2DONK.js";
7
7
 
8
8
  // src/mcp-server.ts
9
9
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyphaene/hexa-ts-kit",
3
- "version": "1.12.1",
3
+ "version": "1.13.0",
4
4
  "description": "TypeScript dev kit for Claude Code agents: architecture linting, scaffolding, knowledge analysis",
5
5
  "type": "module",
6
6
  "bin": {
@@ -47,9 +47,7 @@
47
47
  "homepage": "https://github.com/hyphaene/hexa-ts-kit#readme",
48
48
  "dependencies": {
49
49
  "@modelcontextprotocol/sdk": "^1.25.1",
50
- "@typescript-eslint/parser": "^8.50.1",
51
- "@typescript-eslint/typescript-estree": "^8.50.1",
52
- "commander": "^12.1.0",
50
+ "commander": "^14.0.3",
53
51
  "fast-glob": "^3.3.2",
54
52
  "gray-matter": "^4.0.3",
55
53
  "ink": "^6.6.0",
@@ -60,20 +58,19 @@
60
58
  "picocolors": "^1.1.1",
61
59
  "react": "^19.2.4",
62
60
  "ts-morph": "^27.0.2",
63
- "vue-eslint-parser": "^10.2.0",
64
61
  "yaml": "^2.8.2",
65
62
  "zod": "^4.3.4"
66
63
  },
67
64
  "devDependencies": {
68
65
  "@semantic-release/changelog": "^6.0.3",
69
66
  "@semantic-release/git": "^10.0.1",
70
- "@types/node": "^22.10.2",
67
+ "@types/node": "^25.2.3",
71
68
  "@types/react": "^19.2.10",
72
- "@vitest/coverage-v8": "^3.1.4",
69
+ "@vitest/coverage-v8": "^4.0.18",
73
70
  "semantic-release": "^25.0.2",
74
71
  "tsup": "^8.3.5",
75
72
  "typescript": "^5.7.2",
76
- "vitest": "^3.1.4"
73
+ "vitest": "^4.0.18"
77
74
  },
78
75
  "engines": {
79
76
  "node": ">=20"