gitnexus 1.6.13-rc.21 → 1.6.13-rc.22

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.
@@ -105,6 +105,32 @@ export function csharpScanToEvidence(scan) {
105
105
  truncated: scan.truncated,
106
106
  };
107
107
  }
108
+ /**
109
+ * Declaration view for explicit import resolve. `origin: 'directories'`
110
+ * is grouping-only. A hand-built `{ targets }` with no origin stays a
111
+ * declaration so existing fixtures keep working.
112
+ */
113
+ export function coerceDeclaredSwiftTargets(resolutionConfig) {
114
+ const config = resolutionConfig;
115
+ if (config == null)
116
+ return null;
117
+ if (config.origin === 'directories')
118
+ return null;
119
+ if (config.declaredTargets instanceof Map)
120
+ return config.declaredTargets;
121
+ if (config.targets instanceof Map)
122
+ return config.targets;
123
+ return null;
124
+ }
125
+ /** Segment-boundary prefix for a Package.swift `path:`. `"."` / `"./"` is the package root. */
126
+ export function swiftDeclaredTargetPrefix(dir) {
127
+ let norm = dir.replace(/\\/g, '/');
128
+ while (norm.startsWith('./')) {
129
+ norm = norm.slice(2);
130
+ }
131
+ norm = norm.replace(/\/+$/, '');
132
+ return norm === '' || norm === '.' ? '' : `${norm}/`;
133
+ }
108
134
  // ============================================================================
109
135
  // LANGUAGE-SPECIFIC CONFIG LOADERS
110
136
  // ============================================================================
@@ -413,10 +439,757 @@ async function collectDeclaredNamespaces(filePath, declaredNamespaces, rootNames
413
439
  // import whose namespace was dropped.
414
440
  return structure.incomplete ? 'truncated' : 'ok';
415
441
  }
416
- export async function loadSwiftPackageConfig(repoRoot) {
417
- // Swift imports are module-name based (e.g., `import SiuperModel`)
418
- // SPM convention: Sources/<TargetName>/ or Package/Sources/<TargetName>/
419
- // We scan for these directories to build a target map
442
+ const SWIFT_SOURCE_FACTORY_NAMES = ['target', 'executableTarget', 'testTarget', 'macro'];
443
+ const SWIFT_SKIP_FACTORY_NAMES = ['binaryTarget', 'plugin', 'systemLibrary'];
444
+ const SWIFT_SKIP_FACTORIES = new Set(SWIFT_SKIP_FACTORY_NAMES);
445
+ const SWIFT_FACTORY_RE = new RegExp(`\\.(${[...SWIFT_SOURCE_FACTORY_NAMES, ...SWIFT_SKIP_FACTORY_NAMES].join('|')})\\s*\\(`, 'g');
446
+ function extractBalancedParen(source, openIndex) {
447
+ let depth = 0;
448
+ let inString = null;
449
+ let escape = false;
450
+ let inLineComment = false;
451
+ let blockCommentDepth = 0;
452
+ for (let i = openIndex; i < source.length; i++) {
453
+ const ch = source[i];
454
+ const next = source[i + 1];
455
+ if (inLineComment) {
456
+ if (ch === '\n')
457
+ inLineComment = false;
458
+ continue;
459
+ }
460
+ if (blockCommentDepth > 0) {
461
+ if (ch === '*' && next === '/') {
462
+ blockCommentDepth--;
463
+ i++;
464
+ }
465
+ else if (ch === '/' && next === '*') {
466
+ blockCommentDepth++;
467
+ i++;
468
+ }
469
+ continue;
470
+ }
471
+ if (inString !== null) {
472
+ if (escape) {
473
+ escape = false;
474
+ continue;
475
+ }
476
+ if (ch === '\\') {
477
+ escape = true;
478
+ continue;
479
+ }
480
+ if (ch === inString)
481
+ inString = null;
482
+ continue;
483
+ }
484
+ if (ch === '"' || ch === "'") {
485
+ inString = ch;
486
+ continue;
487
+ }
488
+ if (ch === '/' && next === '/') {
489
+ // `https://` lives inside a string, already excluded above.
490
+ inLineComment = true;
491
+ i++;
492
+ continue;
493
+ }
494
+ else if (ch === '/' && next === '*') {
495
+ blockCommentDepth = 1;
496
+ i++;
497
+ continue;
498
+ }
499
+ if (ch === '(')
500
+ depth++;
501
+ else if (ch === ')') {
502
+ depth--;
503
+ if (depth === 0)
504
+ return source.slice(openIndex + 1, i);
505
+ }
506
+ }
507
+ return null;
508
+ }
509
+ function isSwiftIdentCont(ch) {
510
+ return ch !== undefined && /[A-Za-z0-9_]/.test(ch);
511
+ }
512
+ function skipSwiftWsAndComments(source, start) {
513
+ let i = start;
514
+ while (i < source.length) {
515
+ const ch = source[i];
516
+ const next = source[i + 1];
517
+ if (/\s/.test(ch)) {
518
+ i++;
519
+ continue;
520
+ }
521
+ if (ch === '/' && next === '/') {
522
+ const nl = source.indexOf('\n', i + 2);
523
+ if (nl === -1)
524
+ return null;
525
+ i = nl + 1;
526
+ continue;
527
+ }
528
+ if (ch === '/' && next === '*') {
529
+ let depth = 1;
530
+ i += 2;
531
+ while (i < source.length && depth > 0) {
532
+ if (source[i] === '/' && source[i + 1] === '*') {
533
+ depth++;
534
+ i += 2;
535
+ }
536
+ else if (source[i] === '*' && source[i + 1] === '/') {
537
+ depth--;
538
+ i += 2;
539
+ }
540
+ else {
541
+ i++;
542
+ }
543
+ }
544
+ if (depth !== 0)
545
+ return null;
546
+ continue;
547
+ }
548
+ return i;
549
+ }
550
+ return null;
551
+ }
552
+ /** First `name:` / `path:` string outside comments. Escapes and interpolations are unreadable. */
553
+ function readSwiftFactoryField(block, field) {
554
+ let inString = null;
555
+ let escape = false;
556
+ let inLineComment = false;
557
+ let blockCommentDepth = 0;
558
+ for (let i = 0; i < block.length; i++) {
559
+ const ch = block[i];
560
+ const next = block[i + 1];
561
+ if (inLineComment) {
562
+ if (ch === '\n')
563
+ inLineComment = false;
564
+ continue;
565
+ }
566
+ if (blockCommentDepth > 0) {
567
+ if (ch === '*' && next === '/') {
568
+ blockCommentDepth--;
569
+ i++;
570
+ }
571
+ else if (ch === '/' && next === '*') {
572
+ blockCommentDepth++;
573
+ i++;
574
+ }
575
+ continue;
576
+ }
577
+ if (inString !== null) {
578
+ if (escape) {
579
+ escape = false;
580
+ continue;
581
+ }
582
+ if (ch === '\\') {
583
+ escape = true;
584
+ continue;
585
+ }
586
+ if (ch === inString)
587
+ inString = null;
588
+ continue;
589
+ }
590
+ if (ch === '"' || ch === "'") {
591
+ inString = ch;
592
+ continue;
593
+ }
594
+ if (ch === '/' && next === '/') {
595
+ inLineComment = true;
596
+ i++;
597
+ continue;
598
+ }
599
+ if (ch === '/' && next === '*') {
600
+ blockCommentDepth = 1;
601
+ i++;
602
+ continue;
603
+ }
604
+ if (!/[A-Za-z_]/.test(ch))
605
+ continue;
606
+ let j = i + 1;
607
+ while (j < block.length && isSwiftIdentCont(block[j]))
608
+ j++;
609
+ if (block.slice(i, j) !== field) {
610
+ i = j - 1;
611
+ continue;
612
+ }
613
+ const colonAt = skipSwiftWsAndComments(block, j);
614
+ if (colonAt === null || block[colonAt] !== ':') {
615
+ i = j - 1;
616
+ continue;
617
+ }
618
+ const valueAt = skipSwiftWsAndComments(block, colonAt + 1);
619
+ if (valueAt === null)
620
+ return { value: undefined, keyPresent: true };
621
+ const quote = block[valueAt];
622
+ if (quote !== '"' && quote !== "'")
623
+ return { value: undefined, keyPresent: true };
624
+ const parsed = readSwiftSimpleQuotedString(block, valueAt);
625
+ if (parsed === null)
626
+ return { value: undefined, keyPresent: true };
627
+ return { value: parsed, keyPresent: true };
628
+ }
629
+ return { value: undefined, keyPresent: false };
630
+ }
631
+ /** Quoted literal with no escapes. Any `\` (including `\u{…}` and `\(`) is unreadable. */
632
+ function readSwiftSimpleQuotedString(source, openIndex) {
633
+ const quote = source[openIndex];
634
+ let i = openIndex + 1;
635
+ while (i < source.length) {
636
+ const ch = source[i];
637
+ if (ch === '\\')
638
+ return null;
639
+ if (ch === quote)
640
+ return source.slice(openIndex + 1, i);
641
+ if (ch === '\n')
642
+ return null;
643
+ i++;
644
+ }
645
+ return null;
646
+ }
647
+ function swiftManifestHasCompletenessHazard(source) {
648
+ let inString = null;
649
+ let escape = false;
650
+ let inLineComment = false;
651
+ let blockCommentDepth = 0;
652
+ let atLineStart = true;
653
+ for (let i = 0; i < source.length; i++) {
654
+ const ch = source[i];
655
+ const next = source[i + 1];
656
+ if (inLineComment) {
657
+ if (ch === '\n') {
658
+ inLineComment = false;
659
+ atLineStart = true;
660
+ }
661
+ continue;
662
+ }
663
+ if (blockCommentDepth > 0) {
664
+ if (ch === '*' && next === '/') {
665
+ blockCommentDepth--;
666
+ i++;
667
+ }
668
+ else if (ch === '/' && next === '*') {
669
+ blockCommentDepth++;
670
+ i++;
671
+ }
672
+ else if (ch === '\n') {
673
+ atLineStart = true;
674
+ }
675
+ continue;
676
+ }
677
+ if (inString !== null) {
678
+ if (escape) {
679
+ escape = false;
680
+ continue;
681
+ }
682
+ if (ch === '\\') {
683
+ escape = true;
684
+ continue;
685
+ }
686
+ if (ch === inString)
687
+ inString = null;
688
+ else if (ch === '\n')
689
+ atLineStart = true;
690
+ continue;
691
+ }
692
+ if (ch === '"' || ch === "'") {
693
+ inString = ch;
694
+ atLineStart = false;
695
+ continue;
696
+ }
697
+ if (ch === '/' && next === '/') {
698
+ inLineComment = true;
699
+ i++;
700
+ atLineStart = false;
701
+ continue;
702
+ }
703
+ if (ch === '/' && next === '*') {
704
+ blockCommentDepth = 1;
705
+ i++;
706
+ atLineStart = false;
707
+ continue;
708
+ }
709
+ if (ch === '\n') {
710
+ atLineStart = true;
711
+ continue;
712
+ }
713
+ if (atLineStart && /\s/.test(ch))
714
+ continue;
715
+ if (atLineStart && ch === '#') {
716
+ if (source.startsWith('if', i + 1) && !isSwiftIdentCont(source[i + 3]))
717
+ return true;
718
+ if (source.startsWith('elseif', i + 1) && !isSwiftIdentCont(source[i + 7]))
719
+ return true;
720
+ }
721
+ atLineStart = false;
722
+ }
723
+ return false;
724
+ }
725
+ function newSwiftCommentScan() {
726
+ return { i: 0, inString: null, escape: false, inLineComment: false, blockCommentDepth: 0 };
727
+ }
728
+ /** Resume the comment/string walk up to `upTo`. Matches are left-to-right, so this is O(n) over the file. */
729
+ function advanceSwiftCommentScan(source, state, upTo) {
730
+ let { i, inString, escape, inLineComment, blockCommentDepth } = state;
731
+ for (; i < upTo; i++) {
732
+ const ch = source[i];
733
+ const next = source[i + 1];
734
+ if (inLineComment) {
735
+ if (ch === '\n')
736
+ inLineComment = false;
737
+ continue;
738
+ }
739
+ if (blockCommentDepth > 0) {
740
+ if (ch === '*' && next === '/') {
741
+ blockCommentDepth--;
742
+ i++;
743
+ }
744
+ else if (ch === '/' && next === '*') {
745
+ blockCommentDepth++;
746
+ i++;
747
+ }
748
+ continue;
749
+ }
750
+ if (inString !== null) {
751
+ if (escape) {
752
+ escape = false;
753
+ continue;
754
+ }
755
+ if (ch === '\\') {
756
+ escape = true;
757
+ continue;
758
+ }
759
+ if (ch === inString)
760
+ inString = null;
761
+ continue;
762
+ }
763
+ if (ch === '"' || ch === "'") {
764
+ inString = ch;
765
+ continue;
766
+ }
767
+ if (ch === '/' && next === '/') {
768
+ inLineComment = true;
769
+ i++;
770
+ continue;
771
+ }
772
+ if (ch === '/' && next === '*') {
773
+ blockCommentDepth = 1;
774
+ i++;
775
+ }
776
+ }
777
+ state.i = i;
778
+ state.inString = inString;
779
+ state.escape = escape;
780
+ state.inLineComment = inLineComment;
781
+ state.blockCommentDepth = blockCommentDepth;
782
+ }
783
+ function swiftPathIsUnreadable(customPath, hasPathKey) {
784
+ if (!hasPathKey)
785
+ return false;
786
+ return customPath === undefined || customPath === '' || customPath.includes('\\(');
787
+ }
788
+ /** Heuristic Package.swift scan. Never shells out to `swift package dump-package`. */
789
+ export function parseSwiftPackageManifest(source) {
790
+ const targets = new Map();
791
+ if (swiftManifestHasCompletenessHazard(source)) {
792
+ return { targets, complete: false };
793
+ }
794
+ const packageTargets = inspectSwiftPackageTargets(source);
795
+ SWIFT_FACTORY_RE.lastIndex = 0;
796
+ let match;
797
+ let sawUnreadableFactory = false;
798
+ const commentScan = newSwiftCommentScan();
799
+ let coveredEnd = -1;
800
+ while ((match = SWIFT_FACTORY_RE.exec(source)) !== null) {
801
+ advanceSwiftCommentScan(source, commentScan, match.index);
802
+ if (commentScan.inLineComment ||
803
+ commentScan.blockCommentDepth > 0 ||
804
+ commentScan.inString !== null) {
805
+ continue;
806
+ }
807
+ if (packageTargets.sawPackage &&
808
+ !packageTargets.arraySpans.some(([lo, hi]) => match.index >= lo && match.index <= hi)) {
809
+ continue;
810
+ }
811
+ if (match.index > 0 && match.index < coveredEnd)
812
+ continue;
813
+ const kind = match[1];
814
+ const paren = source.indexOf('(', match.index);
815
+ const block = extractBalancedParen(source, paren);
816
+ if (block === null) {
817
+ sawUnreadableFactory = true;
818
+ continue;
819
+ }
820
+ coveredEnd = Math.max(coveredEnd, paren + 1 + block.length + 1);
821
+ if (SWIFT_SKIP_FACTORIES.has(kind))
822
+ continue;
823
+ const nameField = readSwiftFactoryField(block, 'name');
824
+ if (nameField.value === undefined || nameField.value === '') {
825
+ sawUnreadableFactory = true;
826
+ continue;
827
+ }
828
+ const name = nameField.value;
829
+ const pathField = readSwiftFactoryField(block, 'path');
830
+ const customPath = pathField.value;
831
+ if (swiftPathIsUnreadable(customPath, pathField.keyPresent)) {
832
+ sawUnreadableFactory = true;
833
+ continue;
834
+ }
835
+ const dir = customPath ?? (kind === 'testTarget' ? `Tests/${name}` : `Sources/${name}`);
836
+ const existing = targets.get(name);
837
+ if (existing === undefined) {
838
+ targets.set(name, dir);
839
+ }
840
+ else if (customPath !== undefined && existing === `Sources/${name}`) {
841
+ // A later `.target(name:path:)` wins over an earlier same-name
842
+ // factory that only implied the default path.
843
+ targets.set(name, customPath);
844
+ }
845
+ }
846
+ return {
847
+ targets,
848
+ complete: !sawUnreadableFactory && !packageTargets.helperBuilt,
849
+ };
850
+ }
851
+ const SWIFT_ALL_FACTORY_NAMES = new Set([
852
+ ...SWIFT_SOURCE_FACTORY_NAMES,
853
+ ...SWIFT_SKIP_FACTORY_NAMES,
854
+ ]);
855
+ /** Locate `Package(...)`'s `targets:` argument. Product `targets:` stay nested. */
856
+ function inspectSwiftPackageTargets(source) {
857
+ const arraySpans = [];
858
+ let helperBuilt = false;
859
+ const seen = { package: false };
860
+ const unreadable = forEachSwiftPackageArgs(source, (args, argsStart) => {
861
+ const found = inspectPackageTargetsArg(args);
862
+ if (found.helperBuilt) {
863
+ helperBuilt = true;
864
+ return true;
865
+ }
866
+ if (found.arrayStart !== null && found.arrayEnd !== null) {
867
+ arraySpans.push([argsStart + found.arrayStart, argsStart + found.arrayEnd]);
868
+ }
869
+ return false;
870
+ }, seen);
871
+ return { helperBuilt: helperBuilt || unreadable, sawPackage: seen.package, arraySpans };
872
+ }
873
+ /** Walk `Package(` calls outside comments/strings. Unclosed `Package(` is incomplete. */
874
+ function forEachSwiftPackageArgs(source, visit, seen) {
875
+ let inString = null;
876
+ let escape = false;
877
+ let inLineComment = false;
878
+ let blockCommentDepth = 0;
879
+ for (let i = 0; i < source.length; i++) {
880
+ const ch = source[i];
881
+ const next = source[i + 1];
882
+ if (inLineComment) {
883
+ if (ch === '\n')
884
+ inLineComment = false;
885
+ continue;
886
+ }
887
+ if (blockCommentDepth > 0) {
888
+ if (ch === '*' && next === '/') {
889
+ blockCommentDepth--;
890
+ i++;
891
+ }
892
+ else if (ch === '/' && next === '*') {
893
+ blockCommentDepth++;
894
+ i++;
895
+ }
896
+ continue;
897
+ }
898
+ if (inString !== null) {
899
+ if (escape) {
900
+ escape = false;
901
+ continue;
902
+ }
903
+ if (ch === '\\') {
904
+ escape = true;
905
+ continue;
906
+ }
907
+ if (ch === inString)
908
+ inString = null;
909
+ continue;
910
+ }
911
+ if (ch === '"' || ch === "'") {
912
+ inString = ch;
913
+ continue;
914
+ }
915
+ if (ch === '/' && next === '/') {
916
+ inLineComment = true;
917
+ i++;
918
+ continue;
919
+ }
920
+ if (ch === '/' && next === '*') {
921
+ blockCommentDepth = 1;
922
+ i++;
923
+ continue;
924
+ }
925
+ if (!source.startsWith('Package', i) ||
926
+ isSwiftIdentCont(source[i + 7]) ||
927
+ (i > 0 && isSwiftIdentCont(source[i - 1]))) {
928
+ continue;
929
+ }
930
+ const parenAt = skipSwiftWsAndComments(source, i + 7);
931
+ if (parenAt === null || source[parenAt] !== '(')
932
+ continue;
933
+ seen.package = true;
934
+ const args = extractBalancedParen(source, parenAt);
935
+ if (args === null)
936
+ return true;
937
+ if (visit(args, parenAt + 1))
938
+ return true;
939
+ i = parenAt + args.length + 1;
940
+ }
941
+ return false;
942
+ }
943
+ function inspectPackageTargetsArg(args) {
944
+ let inString = null;
945
+ let escape = false;
946
+ let inLineComment = false;
947
+ let blockCommentDepth = 0;
948
+ let paren = 0;
949
+ for (let i = 0; i < args.length; i++) {
950
+ const ch = args[i];
951
+ const next = args[i + 1];
952
+ if (inLineComment) {
953
+ if (ch === '\n')
954
+ inLineComment = false;
955
+ continue;
956
+ }
957
+ if (blockCommentDepth > 0) {
958
+ if (ch === '*' && next === '/') {
959
+ blockCommentDepth--;
960
+ i++;
961
+ }
962
+ else if (ch === '/' && next === '*') {
963
+ blockCommentDepth++;
964
+ i++;
965
+ }
966
+ continue;
967
+ }
968
+ if (inString !== null) {
969
+ if (escape) {
970
+ escape = false;
971
+ continue;
972
+ }
973
+ if (ch === '\\') {
974
+ escape = true;
975
+ continue;
976
+ }
977
+ if (ch === inString)
978
+ inString = null;
979
+ continue;
980
+ }
981
+ if (ch === '"' || ch === "'") {
982
+ inString = ch;
983
+ continue;
984
+ }
985
+ if (ch === '/' && next === '/') {
986
+ inLineComment = true;
987
+ i++;
988
+ continue;
989
+ }
990
+ if (ch === '/' && next === '*') {
991
+ blockCommentDepth = 1;
992
+ i++;
993
+ continue;
994
+ }
995
+ if (ch === '(') {
996
+ paren++;
997
+ continue;
998
+ }
999
+ if (ch === ')') {
1000
+ paren--;
1001
+ continue;
1002
+ }
1003
+ if (paren !== 0)
1004
+ continue;
1005
+ if (!args.startsWith('targets', i) ||
1006
+ isSwiftIdentCont(args[i + 7]) ||
1007
+ (i > 0 && isSwiftIdentCont(args[i - 1]))) {
1008
+ continue;
1009
+ }
1010
+ const colonAt = skipSwiftWsAndComments(args, i + 7);
1011
+ if (colonAt === null || args[colonAt] !== ':') {
1012
+ i += 6;
1013
+ continue;
1014
+ }
1015
+ return classifyPackageTargetsValue(args, colonAt + 1);
1016
+ }
1017
+ return { helperBuilt: false, arrayStart: null, arrayEnd: null };
1018
+ }
1019
+ function classifyPackageTargetsValue(args, afterColon) {
1020
+ const start = skipSwiftWsAndComments(args, afterColon);
1021
+ if (start === null)
1022
+ return { helperBuilt: true, arrayStart: null, arrayEnd: null };
1023
+ if (args[start] === '[') {
1024
+ const close = matchSwiftSquare(args, start);
1025
+ if (close === null)
1026
+ return { helperBuilt: true, arrayStart: null, arrayEnd: null };
1027
+ const next = skipSwiftWsAndComments(args, close + 1);
1028
+ if (next !== null && args[next] === '+') {
1029
+ return { helperBuilt: true, arrayStart: start, arrayEnd: close };
1030
+ }
1031
+ if (packageTargetsArrayHasComputed(args, start, close)) {
1032
+ return { helperBuilt: true, arrayStart: start, arrayEnd: close };
1033
+ }
1034
+ return { helperBuilt: false, arrayStart: start, arrayEnd: close };
1035
+ }
1036
+ return { helperBuilt: true, arrayStart: null, arrayEnd: null };
1037
+ }
1038
+ function packageTargetsArrayHasComputed(source, open, close) {
1039
+ let inString = null;
1040
+ let escape = false;
1041
+ let inLineComment = false;
1042
+ let blockCommentDepth = 0;
1043
+ let paren = 0;
1044
+ let bracket = 0;
1045
+ for (let i = open; i < close; i++) {
1046
+ const ch = source[i];
1047
+ const next = source[i + 1];
1048
+ if (inLineComment) {
1049
+ if (ch === '\n')
1050
+ inLineComment = false;
1051
+ continue;
1052
+ }
1053
+ if (blockCommentDepth > 0) {
1054
+ if (ch === '*' && next === '/') {
1055
+ blockCommentDepth--;
1056
+ i++;
1057
+ }
1058
+ else if (ch === '/' && next === '*') {
1059
+ blockCommentDepth++;
1060
+ i++;
1061
+ }
1062
+ continue;
1063
+ }
1064
+ if (inString !== null) {
1065
+ if (escape) {
1066
+ escape = false;
1067
+ continue;
1068
+ }
1069
+ if (ch === '\\') {
1070
+ escape = true;
1071
+ continue;
1072
+ }
1073
+ if (ch === inString)
1074
+ inString = null;
1075
+ continue;
1076
+ }
1077
+ if (ch === '"' || ch === "'") {
1078
+ inString = ch;
1079
+ continue;
1080
+ }
1081
+ if (ch === '/' && next === '/') {
1082
+ inLineComment = true;
1083
+ i++;
1084
+ continue;
1085
+ }
1086
+ if (ch === '/' && next === '*') {
1087
+ blockCommentDepth = 1;
1088
+ i++;
1089
+ continue;
1090
+ }
1091
+ if (ch === '[') {
1092
+ bracket++;
1093
+ continue;
1094
+ }
1095
+ if (ch === ']') {
1096
+ bracket--;
1097
+ continue;
1098
+ }
1099
+ if (ch === '(') {
1100
+ paren++;
1101
+ continue;
1102
+ }
1103
+ if (ch === ')') {
1104
+ paren--;
1105
+ continue;
1106
+ }
1107
+ if (bracket !== 1 || paren !== 0)
1108
+ continue;
1109
+ if (ch === ',' || /\s/.test(ch))
1110
+ continue;
1111
+ if (ch === '.') {
1112
+ let j = i + 1;
1113
+ while (j < close && isSwiftIdentCont(source[j]))
1114
+ j++;
1115
+ const name = source.slice(i + 1, j);
1116
+ const after = skipSwiftWsAndComments(source, j);
1117
+ if (after !== null && source[after] === '(' && SWIFT_ALL_FACTORY_NAMES.has(name)) {
1118
+ const block = extractBalancedParen(source, after);
1119
+ if (block === null)
1120
+ return true;
1121
+ i = after + block.length + 1;
1122
+ continue;
1123
+ }
1124
+ return true;
1125
+ }
1126
+ return true;
1127
+ }
1128
+ return false;
1129
+ }
1130
+ function matchSwiftSquare(source, openIndex) {
1131
+ let depth = 0;
1132
+ let inString = null;
1133
+ let escape = false;
1134
+ let inLineComment = false;
1135
+ let blockCommentDepth = 0;
1136
+ for (let i = openIndex; i < source.length; i++) {
1137
+ const ch = source[i];
1138
+ const next = source[i + 1];
1139
+ if (inLineComment) {
1140
+ if (ch === '\n')
1141
+ inLineComment = false;
1142
+ continue;
1143
+ }
1144
+ if (blockCommentDepth > 0) {
1145
+ if (ch === '*' && next === '/') {
1146
+ blockCommentDepth--;
1147
+ i++;
1148
+ }
1149
+ else if (ch === '/' && next === '*') {
1150
+ blockCommentDepth++;
1151
+ i++;
1152
+ }
1153
+ continue;
1154
+ }
1155
+ if (inString !== null) {
1156
+ if (escape) {
1157
+ escape = false;
1158
+ continue;
1159
+ }
1160
+ if (ch === '\\') {
1161
+ escape = true;
1162
+ continue;
1163
+ }
1164
+ if (ch === inString)
1165
+ inString = null;
1166
+ continue;
1167
+ }
1168
+ if (ch === '"' || ch === "'") {
1169
+ inString = ch;
1170
+ continue;
1171
+ }
1172
+ if (ch === '/' && next === '/') {
1173
+ inLineComment = true;
1174
+ i++;
1175
+ continue;
1176
+ }
1177
+ else if (ch === '/' && next === '*') {
1178
+ blockCommentDepth = 1;
1179
+ i++;
1180
+ continue;
1181
+ }
1182
+ if (ch === '[')
1183
+ depth++;
1184
+ else if (ch === ']') {
1185
+ depth--;
1186
+ if (depth === 0)
1187
+ return i;
1188
+ }
1189
+ }
1190
+ return null;
1191
+ }
1192
+ async function inferSwiftDirectoryTargets(repoRoot) {
420
1193
  const targets = new Map();
421
1194
  const sourceDirs = ['Sources', 'Package/Sources', 'src'];
422
1195
  for (const sourceDir of sourceDirs) {
@@ -433,11 +1206,40 @@ export async function loadSwiftPackageConfig(repoRoot) {
433
1206
  // Directory doesn't exist
434
1207
  }
435
1208
  }
436
- if (targets.size > 0) {
1209
+ return targets;
1210
+ }
1211
+ export async function loadSwiftPackageConfig(repoRoot) {
1212
+ try {
1213
+ const source = await fs.readFile(path.join(repoRoot, 'Package.swift'), 'utf-8');
1214
+ const parsed = parseSwiftPackageManifest(source);
1215
+ if (parsed.complete) {
1216
+ if (isDev) {
1217
+ logger.info(`📦 Loaded ${parsed.targets.size} Swift package targets from Package.swift`);
1218
+ }
1219
+ if (parsed.targets.size > 0) {
1220
+ return {
1221
+ targets: parsed.targets,
1222
+ origin: 'package.swift',
1223
+ declaredTargets: parsed.targets,
1224
+ };
1225
+ }
1226
+ const inferred = await inferSwiftDirectoryTargets(repoRoot);
1227
+ return {
1228
+ targets: inferred,
1229
+ origin: 'package.swift',
1230
+ declaredTargets: parsed.targets,
1231
+ };
1232
+ }
1233
+ }
1234
+ catch {
1235
+ // Missing or unreadable — fall through to inferred folders.
1236
+ }
1237
+ const inferred = await inferSwiftDirectoryTargets(repoRoot);
1238
+ if (inferred.size > 0) {
437
1239
  if (isDev) {
438
- logger.info(`📦 Loaded ${targets.size} Swift package targets`);
1240
+ logger.info(`📦 Inferred ${inferred.size} Swift source folders`);
439
1241
  }
440
- return { targets };
1242
+ return { targets: inferred, origin: 'directories' };
441
1243
  }
442
1244
  return null;
443
1245
  }