@abapify/adt-plugin-gcts 0.4.33 → 0.4.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-CNh9ExCG.mjs";
2
2
  import { createPlugin, registerFormatPlugin } from "@abapify/adt-plugin";
3
- import { AdkClass, AdkDataElement, AdkDomain, AdkFunctionGroup, AdkInterface, AdkPackage, AdkProgram, AdkTable, AdkTableType, createAdk, getMainType, getTypeForKind } from "@abapify/adk";
3
+ import { AdkClass, AdkDataElement, AdkDomain, AdkFunctionGroup, AdkFunctionModule, AdkInterface, AdkPackage, AdkProgram, AdkTable, AdkTableType, createAdk, getMainType, getTypeForKind } from "@abapify/adk";
4
4
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
5
5
  import { dirname, join, resolve, sep } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
@@ -385,7 +385,7 @@ function affHeaderProps(ref) {
385
385
  if (error instanceof Error && error.message.startsWith("No AFF schema")) return [];
386
386
  throw error;
387
387
  }
388
- const header = (schema.properties ?? {}).header;
388
+ const header = schema.properties?.header;
389
389
  return Object.keys(header && typeof header === "object" && header.properties ? header.properties : {});
390
390
  }
391
391
  const ajv = new Ajv2020({
@@ -744,6 +744,56 @@ function companionFiles(object, type, objectName, emitted, suffixToSourceKey) {
744
744
  ...propertyCompanionFiles(payload, objectName, typeLower)
745
745
  ].filter((f) => !emitted.has(f.path));
746
746
  }
747
+ /**
748
+ * Register a handler for an ADK class (preferred — `type` is derived from
749
+ * the static `kind`) or an explicit type string (for types without an ADK
750
+ * class, e.g. DOMA/DTEL).
751
+ */
752
+ async function resolveSource(entry) {
753
+ return await (typeof entry.content === "function" ? entry.content() : entry.content);
754
+ }
755
+ /** Resolve the metadata file name for an object (custom hook or default). */
756
+ function resolveMetadataFileName(definition, object, objectName, type) {
757
+ if (typeof definition.metadataFileName === "function") return definition.metadataFileName(object);
758
+ return definition.metadataFileName ?? gctsFilename(objectName, type, "metadata");
759
+ }
760
+ /**
761
+ * Definition source entries → serialized files. Skips empty sources;
762
+ * entries are resolved through the lazy `GctsSourceEntry` contract.
763
+ */
764
+ async function serializeSourceFiles(object, definition, type, objectName) {
765
+ const files = [];
766
+ if (definition.getSources) for (const entry of definition.getSources(object)) {
767
+ const content = await resolveSource(entry);
768
+ if (!content) continue;
769
+ files.push({
770
+ path: gctsFilename(objectName, type, "source", entry.suffix, entry.ext),
771
+ content
772
+ });
773
+ }
774
+ else if (definition.getSource) {
775
+ const content = await definition.getSource(object);
776
+ if (content) files.push({
777
+ path: gctsFilename(objectName, type, "source"),
778
+ content
779
+ });
780
+ }
781
+ return files;
782
+ }
783
+ /** Extra files (e.g. FUGR per-function-module files) → serialized files. */
784
+ async function serializeExtraFiles(object, definition) {
785
+ if (!definition.getExtraFiles) return [];
786
+ const files = [];
787
+ for (const extra of await definition.getExtraFiles(object)) {
788
+ const content = await resolveSource(extra);
789
+ if (!content) continue;
790
+ files.push({
791
+ path: extra.path,
792
+ content
793
+ });
794
+ }
795
+ return files;
796
+ }
747
797
  function createHandler(adkClassOrType, definition) {
748
798
  let type;
749
799
  if (typeof adkClassOrType === "string") type = adkClassOrType;
@@ -754,9 +804,6 @@ function createHandler(adkClassOrType, definition) {
754
804
  type = derived;
755
805
  }
756
806
  const fileExtension = getMainType(type).toLowerCase();
757
- async function resolveSource(entry) {
758
- return await (typeof entry.content === "function" ? entry.content() : entry.content);
759
- }
760
807
  const handler = {
761
808
  type,
762
809
  fileExtension,
@@ -766,41 +813,20 @@ function createHandler(adkClassOrType, definition) {
766
813
  },
767
814
  suffixToSourceKey: definition.suffixToSourceKey,
768
815
  async serialize(object) {
769
- const files = [];
770
816
  const objectName = (object.name ?? "").toLowerCase();
771
- if (definition.getSources) for (const entry of definition.getSources(object)) {
772
- const content = await resolveSource(entry);
773
- if (!content) continue;
774
- files.push({
775
- path: gctsFilename(objectName, type, "source", entry.suffix, entry.ext),
776
- content
777
- });
778
- }
779
- else if (definition.getSource) {
780
- const content = await definition.getSource(object);
781
- if (content) files.push({
782
- path: gctsFilename(objectName, type, "source"),
783
- content
784
- });
785
- }
817
+ const files = await serializeSourceFiles(object, definition, type, objectName);
786
818
  const metadata = definition.toMetadata(object);
787
- const metadataFile = typeof definition.metadataFileName === "function" ? definition.metadataFileName(object) : definition.metadataFileName ?? gctsFilename(objectName, type, "metadata");
788
- files.push({
789
- path: metadataFile,
819
+ const metadataFile = {
820
+ path: resolveMetadataFileName(definition, object, objectName, type),
790
821
  content: buildMetadataFile(metadata)
791
- });
792
- if (definition.getExtraFiles) {
793
- const extras = await definition.getExtraFiles(object);
794
- for (const extra of extras) {
795
- const content = await resolveSource(extra);
796
- if (!content) continue;
797
- files.push({
798
- path: extra.path,
799
- content
800
- });
801
- }
802
- }
803
- files.push(...companionFiles(object, type, objectName, new Set(files.map((f) => f.path)), definition.suffixToSourceKey));
822
+ };
823
+ const extraFiles = await serializeExtraFiles(object, definition);
824
+ const emitted = /* @__PURE__ */ new Set([
825
+ ...files.map((f) => f.path),
826
+ metadataFile.path,
827
+ ...extraFiles.map((f) => f.path)
828
+ ]);
829
+ files.push(metadataFile, ...extraFiles, ...companionFiles(object, type, objectName, emitted, definition.suffixToSourceKey));
804
830
  return files;
805
831
  },
806
832
  fromAbapGit: definition.fromMetadata ? (values) => definition.fromMetadata(values) : void 0,
@@ -950,7 +976,6 @@ const interfaceHandler = createHandler(AdkInterface, {
950
976
  * generalInformation: { programType, programStatus, fixPointArithmetic?, ... }
951
977
  * }
952
978
  */
953
- /** ADK sourceObjectStatus → AFF programStatus */
954
979
  const STATUS_TO_AFF = {
955
980
  SAPStandardProduction: "sapProductionProgram",
956
981
  customerProduction: "customerProductionProgram",
@@ -977,7 +1002,7 @@ const programHandler = createHandler(AdkProgram, {
977
1002
  generalInformation
978
1003
  };
979
1004
  },
980
- getSource: (obj) => obj.getSource(),
1005
+ getSource: (prog) => prog.getSource(),
981
1006
  fromMetadata: (meta) => ({
982
1007
  name: "",
983
1008
  type: "PROG/P",
@@ -1031,6 +1056,55 @@ const packageHandler = createHandler(AdkPackage, {
1031
1056
  })
1032
1057
  });
1033
1058
 
1059
+ // src/lib/handlers/objects/ddic-data-type.ts
1060
+ const dataTypes = {
1061
+ ACCP: true,
1062
+ CHAR: true,
1063
+ CLNT: true,
1064
+ CUKY: true,
1065
+ CURR: true,
1066
+ DF16_DEC: true,
1067
+ DF16_RAW: true,
1068
+ DF16_SCL: true,
1069
+ DECFLOAT16: true,
1070
+ DF34_DEC: true,
1071
+ DF34_RAW: true,
1072
+ DF34_SCL: true,
1073
+ DECFLOAT34: true,
1074
+ DATS: true,
1075
+ DATN: true,
1076
+ DEC: true,
1077
+ FLTP: true,
1078
+ GEOM_EWKB: true,
1079
+ INT1: true,
1080
+ INT2: true,
1081
+ INT4: true,
1082
+ INT8: true,
1083
+ LANG: true,
1084
+ LCHR: true,
1085
+ LRAW: true,
1086
+ NUMC: true,
1087
+ PREC: true,
1088
+ QUAN: true,
1089
+ RAW: true,
1090
+ RAWSTRING: true,
1091
+ SSTRING: true,
1092
+ STRING: true,
1093
+ TIMS: true,
1094
+ TIMN: true,
1095
+ UNIT: true,
1096
+ UTCLONG: true,
1097
+ VARC: true
1098
+ };
1099
+ /** Validate the open ADT type code before writing the narrower AFF enum. */
1100
+ function affDataType(value) {
1101
+ if (isAffDataType(value)) return value;
1102
+ throw new Error(`Unsupported AFF DDIC data type: ${String(value)}`);
1103
+ }
1104
+ function isAffDataType(value) {
1105
+ return typeof value === "string" && Object.hasOwn(dataTypes, value);
1106
+ }
1107
+
1034
1108
  // src/lib/handlers/objects/doma.ts
1035
1109
  /**
1036
1110
  * DOMA (domain) handler for gCTS / AFF format.
@@ -1048,16 +1122,16 @@ const packageHandler = createHandler(AdkPackage, {
1048
1122
  const domainHandler = createHandler(AdkDomain, {
1049
1123
  toMetadata(doma) {
1050
1124
  const data = doma.dataSync;
1051
- const content = data.content ?? {};
1125
+ const content = data.content;
1052
1126
  const lang = (data.language ?? data.masterLanguage ?? "").toLowerCase();
1053
- const ti = content.typeInformation ?? {};
1054
- const oi = content.outputInformation ?? {};
1055
- const vi = content.valueInformation ?? {};
1127
+ const ti = content?.typeInformation;
1128
+ const oi = content?.outputInformation;
1129
+ const vi = content?.valueInformation;
1056
1130
  const format = {
1057
- dataType: ti.datatype ?? "CHAR",
1058
- length: ti.length ?? 1
1131
+ dataType: affDataType(ti?.datatype ?? "CHAR"),
1132
+ length: ti?.length ?? 1
1059
1133
  };
1060
- if (typeof ti.decimals === "number") format.decimals = ti.decimals;
1134
+ if (typeof ti?.decimals === "number") format.decimals = ti.decimals;
1061
1135
  const result = {
1062
1136
  formatVersion: "1",
1063
1137
  header: {
@@ -1067,12 +1141,12 @@ const domainHandler = createHandler(AdkDomain, {
1067
1141
  format
1068
1142
  };
1069
1143
  const outChars = {};
1070
- if (typeof oi.length === "number") outChars.length = oi.length;
1071
- if (typeof oi.lowercase === "boolean") outChars.caseSensitive = !oi.lowercase;
1072
- if (oi.conversionExit) outChars.conversionRoutine = oi.conversionExit;
1144
+ if (typeof oi?.length === "number") outChars.length = oi.length;
1145
+ if (typeof oi?.lowercase === "boolean") outChars.caseSensitive = !oi.lowercase;
1146
+ if (oi?.conversionExit) outChars.conversionRoutine = oi.conversionExit;
1073
1147
  if (Object.keys(outChars).length > 0) result.outputCharacteristics = outChars;
1074
- const vtRef = vi.valueTableRef ?? {};
1075
- if (vtRef.name) result.valueTable = { name: vtRef.name };
1148
+ const vtRef = vi?.valueTableRef;
1149
+ if (vtRef?.name) result.valueTable = { name: vtRef.name };
1076
1150
  return result;
1077
1151
  },
1078
1152
  fromMetadata: (meta) => {
@@ -1112,10 +1186,13 @@ const domainHandler = createHandler(AdkDomain, {
1112
1186
  * }
1113
1187
  */
1114
1188
  function buildDataTypeInformation(data) {
1115
- if (data.typeKind === "domain" && data.typeName) return { domainName: data.typeName };
1189
+ if (data.typeKind === "domain" && typeof data.typeName === "string" && data.typeName) return { domainName: data.typeName };
1116
1190
  if (data.dataType) {
1117
- const predefinedType = { dataType: data.dataType };
1118
- if (typeof data.dataTypeLength === "number") predefinedType.length = data.dataTypeLength;
1191
+ if (typeof data.dataTypeLength !== "number") throw new TypeError("AFF predefined DDIC type requires a length");
1192
+ const predefinedType = {
1193
+ dataType: affDataType(data.dataType),
1194
+ length: data.dataTypeLength
1195
+ };
1119
1196
  if (typeof data.dataTypeDecimals === "number") predefinedType.decimals = data.dataTypeDecimals;
1120
1197
  return { predefinedType };
1121
1198
  }
@@ -1144,9 +1221,13 @@ function buildFieldLabels(data) {
1144
1221
  "heading",
1145
1222
  "headingFieldLength"
1146
1223
  ]
1147
- ]) if (data[src]) {
1148
- labels[dst] = data[src];
1149
- if (typeof data[lenKey] === "number") labels[`${dst}Length`] = data[lenKey];
1224
+ ]) {
1225
+ const label = data[src];
1226
+ const length = data[lenKey];
1227
+ if (typeof label === "string" && label) {
1228
+ labels[dst] = label;
1229
+ if (typeof length === "number") labels[`${dst}Length`] = length;
1230
+ }
1150
1231
  }
1151
1232
  return labels;
1152
1233
  }
@@ -1336,6 +1417,42 @@ const functionGroupHandler = createHandler(AdkFunctionGroup, {
1336
1417
  })
1337
1418
  });
1338
1419
 
1420
+ // src/lib/handlers/objects/func.ts
1421
+ /**
1422
+ * FUNC (function module) handler for gCTS / AFF format.
1423
+ *
1424
+ * Projects ADK function module data to the AFF func-v1.json schema shape.
1425
+ * A function module is a child of a function group, but it can also be
1426
+ * serialized as its own ADK object (FUNC) for the gCTS file format.
1427
+ */
1428
+ const functionModuleHandler = createHandler(AdkFunctionModule, {
1429
+ toMetadata(func) {
1430
+ const data = func.dataSync ?? {};
1431
+ (data.language ?? data.masterLanguage ?? "").toLowerCase();
1432
+ const meta = {
1433
+ formatVersion: "1",
1434
+ header: { description: func.description ?? data.description ?? "" },
1435
+ processingType: data.processingType ?? "normal",
1436
+ includeNumber: data.includeNumber ?? "0000"
1437
+ };
1438
+ if (data.rfcProperties && typeof data.rfcProperties === "object") meta.rfcProperties = data.rfcProperties;
1439
+ if (data.updateProperties && typeof data.updateProperties === "object") meta.updateProperties = data.updateProperties;
1440
+ if (typeof data.releaseState === "string") meta.releaseState = data.releaseState;
1441
+ if (typeof data.global === "boolean") meta.global = data.global;
1442
+ if (typeof data.exceptionClasses === "boolean") meta.exceptionClasses = data.exceptionClasses;
1443
+ return meta;
1444
+ },
1445
+ getSource: (func) => typeof func.getSource === "function" ? func.getSource() : "",
1446
+ fromMetadata: (meta) => ({
1447
+ name: "",
1448
+ description: meta.header.description,
1449
+ language: meta.header.originalLanguage?.toUpperCase(),
1450
+ masterLanguage: meta.header.originalLanguage?.toUpperCase(),
1451
+ processingType: meta.processingType,
1452
+ includeNumber: meta.includeNumber
1453
+ })
1454
+ });
1455
+
1339
1456
  // src/lib/handlers/objects/ddls.ts
1340
1457
  /**
1341
1458
  * DDLS (CDS view) handler for gCTS / AFF format.
@@ -1351,6 +1468,56 @@ const functionGroupHandler = createHandler(AdkFunctionGroup, {
1351
1468
  * parentName?: string
1352
1469
  * }
1353
1470
  */
1471
+ const languageVersions = {
1472
+ standard: true,
1473
+ keyUser: true,
1474
+ cloudDevelopment: true
1475
+ };
1476
+ const sourceOrigins = {
1477
+ abapDevelopmentTools: true,
1478
+ customCdsViews: true,
1479
+ customAnalyticalQueries: true,
1480
+ customBusinessObject: true,
1481
+ customCodeList: true,
1482
+ customCdsViewsVariantConfg: true,
1483
+ customFields: true,
1484
+ extensionsForDataSources: true,
1485
+ customSearchModeler: true,
1486
+ serviceConsumptionModel: true
1487
+ };
1488
+ const sourceTypes = {
1489
+ ddicBasedView: true,
1490
+ viewEntity: true,
1491
+ viewExtend: true,
1492
+ viewEntityExtend: true,
1493
+ tableFunction: true,
1494
+ tableEntity: true,
1495
+ abstractEntity: true,
1496
+ customEntity: true,
1497
+ hierarchy: true,
1498
+ projectionView: true,
1499
+ externalEntity: true,
1500
+ unknown: true
1501
+ };
1502
+ /**
1503
+ * ADK exposes these fields as open strings, so the compile-time enum types do
1504
+ * not protect the serialized output. Reject values outside the AFF enum.
1505
+ * Non-strings are rejected before property lookup: an array such as
1506
+ * `['viewEntity']` would otherwise coerce to a valid key and be emitted.
1507
+ */
1508
+ function isAffEnumValue(allowed, value) {
1509
+ return typeof value === "string" && Object.hasOwn(allowed, value);
1510
+ }
1511
+ function mismatchDetail(value) {
1512
+ if (typeof value === "string") return value;
1513
+ if (value === null) return "null";
1514
+ if (Array.isArray(value)) return "array";
1515
+ return typeof value;
1516
+ }
1517
+ function affEnum(allowed, field, value) {
1518
+ if (isAffEnumValue(allowed, value)) return value;
1519
+ throw new Error(`Unsupported AFF DDLS ${field}: ${mismatchDetail(value)}`);
1520
+ }
1354
1521
  const ddlSourceHandler = createHandler("DDLS", {
1355
1522
  toMetadata(obj) {
1356
1523
  const lang = (obj.originalLanguage ?? "").toLowerCase();
@@ -1359,10 +1526,10 @@ const ddlSourceHandler = createHandler("DDLS", {
1359
1526
  header: {
1360
1527
  description: obj.description ?? obj.name ?? "",
1361
1528
  originalLanguage: lang,
1362
- ...obj.abapLanguageVersion ? { abapLanguageVersion: obj.abapLanguageVersion } : {}
1529
+ ...obj.abapLanguageVersion ? { abapLanguageVersion: affEnum(languageVersions, "abapLanguageVersion", obj.abapLanguageVersion) } : {}
1363
1530
  },
1364
- sourceOrigin: obj.sourceOrigin ?? "abapDevelopmentTools",
1365
- sourceType: obj.sourceType ?? "unknown"
1531
+ sourceOrigin: affEnum(sourceOrigins, "sourceOrigin", obj.sourceOrigin ?? "abapDevelopmentTools"),
1532
+ sourceType: affEnum(sourceTypes, "sourceType", obj.sourceType ?? "unknown")
1366
1533
  };
1367
1534
  },
1368
1535
  getSource: (obj) => typeof obj.getSource === "function" ? obj.getSource() : "",
@@ -1703,6 +1870,67 @@ function abapLangVerToAdt(code) {
1703
1870
  return ABAP_LANG_VER_TO_ADT[code] ?? code;
1704
1871
  }
1705
1872
  /**
1873
+ * Register one `.json` file in the object index.
1874
+ *
1875
+ * Packages are NOT deployed (same convention as abapGit's `package.devc.xml`)
1876
+ * — they must exist on target. `{name}.{type}.docu.json` is a DOCU
1877
+ * companion, not a second metadata anchor — without this it can overwrite
1878
+ * metadataFile depending on glob order. Other suffixed `.json` companions
1879
+ * (LIMU sub-object metadata, `{sub}.{stype}`) flow through the source
1880
+ * pipeline with an explicit `json` ext marker.
1881
+ */
1882
+ function registerJsonAnchor(jsonPath, objectMap, supportedTypes) {
1883
+ const filename = jsonPath.split("/").pop();
1884
+ if (filename === "package.devc.json") return;
1885
+ const parsed = parseGctsFilename(filename);
1886
+ if (!parsed) return;
1887
+ if (parsed.extension !== "json") return;
1888
+ if (!supportedTypes.has(parsed.type.toLowerCase())) return;
1889
+ const key = `${parsed.name.toUpperCase()}:${parsed.type.toUpperCase()}`;
1890
+ if (!objectMap.has(key)) objectMap.set(key, {
1891
+ name: parsed.name.toUpperCase(),
1892
+ type: parsed.type.toUpperCase(),
1893
+ sourceFiles: [],
1894
+ propertyFiles: []
1895
+ });
1896
+ const entry = objectMap.get(key);
1897
+ if (parsed.suffix === "docu") entry.docuFile = jsonPath;
1898
+ else if (parsed.suffix) entry.sourceFiles.push({
1899
+ path: jsonPath,
1900
+ suffix: parsed.suffix,
1901
+ ext: "json"
1902
+ });
1903
+ else entry.metadataFile = jsonPath;
1904
+ }
1905
+ /**
1906
+ * Register a `.properties` translation companion — routed into
1907
+ * `data.properties` so it round-trips through metadata, not source staging.
1908
+ */
1909
+ function registerPropertyFile(path, objectMap) {
1910
+ const filename = path.split("/").pop();
1911
+ const parsed = parseGctsFilename(filename);
1912
+ if (!parsed) return;
1913
+ const key = `${parsed.name.toUpperCase()}:${parsed.type.toUpperCase()}`;
1914
+ objectMap.get(key)?.propertyFiles.push({
1915
+ path,
1916
+ suffix: parsed.suffix
1917
+ });
1918
+ }
1919
+ /** Register a source file (.abap/.asddls/.asdcls) with its object group. */
1920
+ function registerSourceFile(path, objectMap) {
1921
+ const filename = path.split("/").pop();
1922
+ const parsed = parseGctsFilename(filename);
1923
+ if (!parsed) return;
1924
+ const key = `${parsed.name.toUpperCase()}:${parsed.type.toUpperCase()}`;
1925
+ const obj = objectMap.get(key);
1926
+ if (!obj) return;
1927
+ obj.sourceFiles.push({
1928
+ path,
1929
+ suffix: parsed.suffix,
1930
+ ext: parsed.extension
1931
+ });
1932
+ }
1933
+ /**
1706
1934
  * Deserialise a gCTS/AFF file tree into `AdkObject` instances.
1707
1935
  *
1708
1936
  * @param fileTree Virtual FS abstraction (checkin supplies a real-fs impl).
@@ -1716,56 +1944,10 @@ async function* deserialize(fileTree, client, options) {
1716
1944
  const jsonFiles = await fileTree.glob("**/*.json");
1717
1945
  const supportedTypes = new Set(getSupportedTypes().map((t) => t.toLowerCase()));
1718
1946
  const objectMap = /* @__PURE__ */ new Map();
1719
- for (const jsonPath of jsonFiles) {
1720
- const filename = jsonPath.split("/").pop();
1721
- if (filename === "package.devc.json") continue;
1722
- const parsed = parseGctsFilename(filename);
1723
- if (!parsed) continue;
1724
- if (parsed.extension !== "json") continue;
1725
- if (!supportedTypes.has(parsed.type.toLowerCase())) continue;
1726
- const key = `${parsed.name.toUpperCase()}:${parsed.type.toUpperCase()}`;
1727
- if (!objectMap.has(key)) objectMap.set(key, {
1728
- name: parsed.name.toUpperCase(),
1729
- type: parsed.type.toUpperCase(),
1730
- sourceFiles: [],
1731
- propertyFiles: []
1732
- });
1733
- if (parsed.suffix === "docu") objectMap.get(key).docuFile = jsonPath;
1734
- else if (parsed.suffix) {
1735
- objectMap.get(key).sourceFiles.push({
1736
- path: jsonPath,
1737
- suffix: parsed.suffix,
1738
- ext: "json"
1739
- });
1740
- continue;
1741
- } else objectMap.get(key).metadataFile = jsonPath;
1742
- }
1743
- for (const path of await fileTree.glob("**/*.properties")) {
1744
- const filename = path.split("/").pop();
1745
- const parsed = parseGctsFilename(filename);
1746
- if (!parsed) continue;
1747
- const key = `${parsed.name.toUpperCase()}:${parsed.type.toUpperCase()}`;
1748
- objectMap.get(key)?.propertyFiles.push({
1749
- path,
1750
- suffix: parsed.suffix
1751
- });
1752
- }
1753
- for (const ext of SOURCE_EXTENSIONS) {
1754
- const paths = await fileTree.glob(`**/*.${ext}`);
1755
- for (const path of paths) {
1756
- const filename = path.split("/").pop();
1757
- const parsed = parseGctsFilename(filename);
1758
- if (!parsed) continue;
1759
- const key = `${parsed.name.toUpperCase()}:${parsed.type.toUpperCase()}`;
1760
- const obj = objectMap.get(key);
1761
- if (!obj) continue;
1762
- obj.sourceFiles.push({
1763
- path,
1764
- suffix: parsed.suffix,
1765
- ext: parsed.extension
1766
- });
1767
- }
1768
- }
1947
+ for (const jsonPath of jsonFiles) registerJsonAnchor(jsonPath, objectMap, supportedTypes);
1948
+ for (const path of await fileTree.glob("**/*.properties")) registerPropertyFile(path, objectMap);
1949
+ const pathsByExtension = await Promise.all(Array.from(SOURCE_EXTENSIONS).map((ext) => fileTree.glob(`**/*.${ext}`)));
1950
+ for (const paths of pathsByExtension) for (const path of paths) registerSourceFile(path, objectMap);
1769
1951
  const readThrottled = throttledReader(fileTree);
1770
1952
  const entries = [...objectMap.values()];
1771
1953
  const pending = /* @__PURE__ */ new Map();