gitnexus 1.6.13-rc.40 → 1.6.13-rc.41
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/core/graph/edge-reasons.d.ts +12 -0
- package/dist/core/graph/edge-reasons.js +12 -0
- package/dist/core/ingestion/import-resolvers/configs/swift.d.ts +4 -3
- package/dist/core/ingestion/import-resolvers/configs/swift.js +4 -3
- package/dist/core/ingestion/language-config.d.ts +97 -4
- package/dist/core/ingestion/language-config.js +186 -32
- package/dist/core/ingestion/languages/swift/implicit-imports.d.ts +27 -29
- package/dist/core/ingestion/languages/swift/implicit-imports.js +63 -47
- package/dist/core/ingestion/languages/swift/import-target.d.ts +12 -4
- package/dist/core/ingestion/languages/swift/import-target.js +59 -6
- package/dist/core/ingestion/languages/swift/index.d.ts +1 -1
- package/dist/core/ingestion/languages/swift/index.js +1 -1
- package/dist/core/ingestion/languages/swift/name-fallback-visibility.d.ts +8 -4
- package/dist/core/ingestion/languages/swift/name-fallback-visibility.js +35 -5
- package/dist/core/ingestion/languages/swift/scope-resolver.d.ts +9 -10
- package/dist/core/ingestion/languages/swift/scope-resolver.js +20 -20
- package/dist/core/ingestion/languages/swift/sibling-type-bindings.d.ts +12 -13
- package/dist/core/ingestion/languages/swift/sibling-type-bindings.js +31 -38
- package/dist/core/ingestion/languages/swift/target-grouping.d.ts +56 -37
- package/dist/core/ingestion/languages/swift/target-grouping.js +186 -68
- package/dist/core/ingestion/languages/swift/target-siblings.d.ts +22 -20
- package/dist/core/ingestion/languages/swift/target-siblings.js +66 -40
- package/dist/core/ingestion/languages/swift/workspace-config.d.ts +20 -0
- package/dist/core/ingestion/languages/swift/workspace-config.js +174 -0
- package/dist/core/ingestion/languages/swift/xcode-project.d.ts +52 -0
- package/dist/core/ingestion/languages/swift/xcode-project.js +326 -0
- package/dist/core/lbug/lbug-adapter.js +61 -35
- package/package.json +1 -1
|
@@ -24,3 +24,15 @@
|
|
|
24
24
|
export declare const GLOBAL_NAME_FALLBACK_REASON = "global-name-fallback";
|
|
25
25
|
/** True when this edge's target was guessed by name rather than resolved. */
|
|
26
26
|
export declare const isHeuristicEdgeReason: (reason: string) => boolean;
|
|
27
|
+
/**
|
|
28
|
+
* An IMPORTS edge from a File to the `Module` node of the compiler module it
|
|
29
|
+
* belongs to, for languages where every file of a module sees every other
|
|
30
|
+
* file's declarations with no `import` (whole-module visibility).
|
|
31
|
+
*
|
|
32
|
+
* One edge per member file instead of one per ordered file pair: the pairwise
|
|
33
|
+
* form is n·(n−1) edges and exhausted V8's Map limit on large modules (#3355).
|
|
34
|
+
* Consumers that need "files that see this file" follow the hub: two files are
|
|
35
|
+
* co-members when both have a membership edge to the same `Module` node
|
|
36
|
+
* (`queryImportersBatch`).
|
|
37
|
+
*/
|
|
38
|
+
export declare const MODULE_MEMBERSHIP_REASON = "module-membership";
|
|
@@ -32,3 +32,15 @@ export const GLOBAL_NAME_FALLBACK_REASON = 'global-name-fallback';
|
|
|
32
32
|
const HEURISTIC_EDGE_REASONS = new Set([GLOBAL_NAME_FALLBACK_REASON]);
|
|
33
33
|
/** True when this edge's target was guessed by name rather than resolved. */
|
|
34
34
|
export const isHeuristicEdgeReason = (reason) => HEURISTIC_EDGE_REASONS.has(reason);
|
|
35
|
+
/**
|
|
36
|
+
* An IMPORTS edge from a File to the `Module` node of the compiler module it
|
|
37
|
+
* belongs to, for languages where every file of a module sees every other
|
|
38
|
+
* file's declarations with no `import` (whole-module visibility).
|
|
39
|
+
*
|
|
40
|
+
* One edge per member file instead of one per ordered file pair: the pairwise
|
|
41
|
+
* form is n·(n−1) edges and exhausted V8's Map limit on large modules (#3355).
|
|
42
|
+
* Consumers that need "files that see this file" follow the hub: two files are
|
|
43
|
+
* co-members when both have a membership edge to the same `Module` node
|
|
44
|
+
* (`queryImportersBatch`).
|
|
45
|
+
*/
|
|
46
|
+
export const MODULE_MEMBERSHIP_REASON = 'module-membership';
|
|
@@ -22,9 +22,10 @@
|
|
|
22
22
|
* `allFileList` entries, and the per-target file ORDER follows
|
|
23
23
|
* `allFileList`.
|
|
24
24
|
*
|
|
25
|
-
* Import-config fans a file to every matching declared target.
|
|
26
|
-
* (`
|
|
27
|
-
* is intentional
|
|
25
|
+
* Import-config fans a file to every matching declared target. Module
|
|
26
|
+
* grouping (`groupSwiftFilesByModule`) anchors target paths at the repo root
|
|
27
|
+
* and picks the deepest. That divergence is intentional: this legacy
|
|
28
|
+
* strategy only sees the root declaration map.
|
|
28
29
|
*/
|
|
29
30
|
import type { ImportResolutionConfig, ImportResolverStrategy } from '../types.js';
|
|
30
31
|
/** Swift Package.swift target map resolution strategy. */
|
|
@@ -22,9 +22,10 @@
|
|
|
22
22
|
* `allFileList` entries, and the per-target file ORDER follows
|
|
23
23
|
* `allFileList`.
|
|
24
24
|
*
|
|
25
|
-
* Import-config fans a file to every matching declared target.
|
|
26
|
-
* (`
|
|
27
|
-
* is intentional
|
|
25
|
+
* Import-config fans a file to every matching declared target. Module
|
|
26
|
+
* grouping (`groupSwiftFilesByModule`) anchors target paths at the repo root
|
|
27
|
+
* and picks the deepest. That divergence is intentional: this legacy
|
|
28
|
+
* strategy only sees the root declaration map.
|
|
28
29
|
*/
|
|
29
30
|
import { SupportedLanguages } from '../../../../_shared/index.js';
|
|
30
31
|
import { coerceDeclaredSwiftTargets, swiftDeclaredTargetPrefix } from '../../language-config.js';
|
|
@@ -86,6 +86,43 @@ export interface SwiftPackageConfig {
|
|
|
86
86
|
* binary-only Package.swift does not collapse every file into `__default__`.
|
|
87
87
|
*/
|
|
88
88
|
declaredTargets?: Map<string, string>;
|
|
89
|
+
/**
|
|
90
|
+
* Every Swift module the workspace loader found (root and nested SwiftPM
|
|
91
|
+
* targets, Xcode native targets). When present it is the authority for
|
|
92
|
+
* module membership and `import` resolution; `targets` stays for callers
|
|
93
|
+
* that only know the SwiftPM map.
|
|
94
|
+
*/
|
|
95
|
+
modules?: readonly SwiftModuleSpec[];
|
|
96
|
+
/**
|
|
97
|
+
* True when every manifest and Xcode project was read completely and no
|
|
98
|
+
* module was inferred from folder names, so a name missing from `modules`
|
|
99
|
+
* is an external module (SDK or dependency), not an unread local one.
|
|
100
|
+
*/
|
|
101
|
+
moduleNamesComplete?: boolean;
|
|
102
|
+
/** `sources:` / `exclude:` per target name, relative to the target directory. */
|
|
103
|
+
targetFilters?: Map<string, SwiftTargetFilter>;
|
|
104
|
+
}
|
|
105
|
+
/** One Swift module (compiler unit) found in the workspace. */
|
|
106
|
+
export interface SwiftModuleSpec {
|
|
107
|
+
/** Stable key: a repo-relative SwiftPM target directory, or `xcode:<project>:<target>`. */
|
|
108
|
+
readonly key: string;
|
|
109
|
+
/** Module name as written in `import X`. */
|
|
110
|
+
readonly name: string;
|
|
111
|
+
/** SwiftPM: repo-relative target directory ('' is the repo root). */
|
|
112
|
+
readonly dir?: string;
|
|
113
|
+
/** Xcode: repo-relative member files. */
|
|
114
|
+
readonly files?: readonly string[];
|
|
115
|
+
/** Xcode: repo-relative synchronized folders; files below are members. */
|
|
116
|
+
readonly folders?: readonly string[];
|
|
117
|
+
/**
|
|
118
|
+
* Repo-relative paths this module leaves out: Xcode synchronized-folder
|
|
119
|
+
* exceptions, SwiftPM `exclude:`.
|
|
120
|
+
*/
|
|
121
|
+
readonly excluded?: readonly string[];
|
|
122
|
+
/** SwiftPM `sources:`: repo-relative files or directories; members must be under one. */
|
|
123
|
+
readonly sources?: readonly string[];
|
|
124
|
+
/** False for SwiftPM plugins: modules, but never `import`-able. */
|
|
125
|
+
readonly importable: boolean;
|
|
89
126
|
}
|
|
90
127
|
/**
|
|
91
128
|
* Declaration view for explicit import resolve. `origin: 'directories'`
|
|
@@ -212,12 +249,36 @@ export declare function loadComposerConfig(repoRoot: string): Promise<ComposerCo
|
|
|
212
249
|
* windows (see below); directories are still visited breadth-first.
|
|
213
250
|
*/
|
|
214
251
|
export declare function scanCSharpProject(repoRoot: string): Promise<CSharpProjectScan>;
|
|
215
|
-
/**
|
|
216
|
-
export
|
|
252
|
+
/** Where SwiftPM looks for a target that declares no `path:`. */
|
|
253
|
+
export type SwiftTargetDirKind = 'source' | 'test' | 'plugin';
|
|
254
|
+
export interface SwiftManifestParse {
|
|
255
|
+
/** Target name -> package-relative directory. */
|
|
217
256
|
targets: Map<string, string>;
|
|
257
|
+
/**
|
|
258
|
+
* Targets with no `path:`. Their directory above is the conventional
|
|
259
|
+
* default; `loadSwiftPackageConfig` replaces it with the directory SwiftPM
|
|
260
|
+
* would actually pick (`Sources`, `Source`, `src`, or `srcs`).
|
|
261
|
+
*/
|
|
262
|
+
implicitDirs: Map<string, SwiftTargetDirKind>;
|
|
263
|
+
/** Plugin targets: modules for grouping, never `import`-able. */
|
|
264
|
+
plugins: Set<string>;
|
|
265
|
+
/** `sources:` / `exclude:` per target name, relative to the target directory. */
|
|
266
|
+
filters: Map<string, SwiftTargetFilter>;
|
|
218
267
|
complete: boolean;
|
|
219
|
-
}
|
|
220
|
-
|
|
268
|
+
}
|
|
269
|
+
/** A target's `sources:` / `exclude:` lists; absent means "no filter". */
|
|
270
|
+
export interface SwiftTargetFilter {
|
|
271
|
+
readonly sources?: readonly string[];
|
|
272
|
+
readonly exclude?: readonly string[];
|
|
273
|
+
}
|
|
274
|
+
/** Heuristic Package.swift scan. Never shells out to `swift package dump-package`. */
|
|
275
|
+
export declare function parseSwiftPackageManifest(source: string): SwiftManifestParse;
|
|
276
|
+
/**
|
|
277
|
+
* Load the SwiftPM config of the package at `packageDir` (repo root when
|
|
278
|
+
* omitted). Target directories stay package-relative; see
|
|
279
|
+
* {@link loadSwiftWorkspaceConfig} for the repo-relative merge.
|
|
280
|
+
*/
|
|
281
|
+
export declare function loadSwiftPackageConfig(repoRoot: string, packageDir?: string): Promise<SwiftPackageConfig | null>;
|
|
221
282
|
/**
|
|
222
283
|
* Load the Zig build configuration a repo's `build.zig.zon` + root `build.zig`
|
|
223
284
|
* declare: `.path` deps (and the roots their own build.zig names) from the
|
|
@@ -298,6 +359,38 @@ export declare function zigPackageFor(index: ZigWorkspaceIndex | null | undefine
|
|
|
298
359
|
* `loadTsconfigPaths` and the repo-walking `loadTsconfigIndex`.
|
|
299
360
|
*/
|
|
300
361
|
export declare function loadZigWorkspaceIndex(repoRoot: string): Promise<ZigWorkspaceIndex | null>;
|
|
362
|
+
/**
|
|
363
|
+
* Normalize a `.path` value from build.zig.zon into a repo-relative form.
|
|
364
|
+
* Returns null for paths that escape the repo root (start with `..`) or
|
|
365
|
+
* are absolute — those point to files we don't index. `.` / `./` normalize
|
|
366
|
+
* to the empty string (the repo root itself). Shared with the import
|
|
367
|
+
* resolver so both sides agree on which deps are in-repo.
|
|
368
|
+
*/
|
|
369
|
+
/**
|
|
370
|
+
* Does this `.path` value point outside the repository BY ITS SPELLING —
|
|
371
|
+
* POSIX absolute (`/dep`), Windows drive-qualified (`C:\dep`, `C:/dep`),
|
|
372
|
+
* root-relative (`\dep`) or UNC (`\\server\share`)?
|
|
373
|
+
*
|
|
374
|
+
* Separators are normalized first so every Windows spelling is visible to the
|
|
375
|
+
* one test. Exported-in-spirit rather than inlined because it must be asked in
|
|
376
|
+
* TWO places and the two must not drift: `normalizeZigDepPath` asks it of the
|
|
377
|
+
* value it is given, and `loadZigBuildConfig` asks it of a NESTED package's
|
|
378
|
+
* value BEFORE prefixing the package directory. That second call is the whole
|
|
379
|
+
* point — prefixing turns `/dep` into `packages/app//dep`, which is relative by
|
|
380
|
+
* inspection, so the check inside `normalizeZigDepPath` no longer sees an
|
|
381
|
+
* absolute path and the empty segment is simply dropped, mapping an
|
|
382
|
+
* out-of-repo dependency onto a real in-repo directory if one happens to exist.
|
|
383
|
+
*
|
|
384
|
+
* `path.posix.join` is NOT a substitute: it strips the leading slash too
|
|
385
|
+
* (`join('packages/app/', '/dep')` is `packages/app/dep`), so it produces the
|
|
386
|
+
* same fabricated path without ever rejecting anything.
|
|
387
|
+
*
|
|
388
|
+
* A `..` prefix is deliberately NOT handled here. `../core` escapes the
|
|
389
|
+
* package but not necessarily the repo, and rebasing it is exactly what the
|
|
390
|
+
* nested-package branch exists to do; `normalizeZigDepPath` rejects the ones
|
|
391
|
+
* that still escape the ROOT after rebasing.
|
|
392
|
+
*/
|
|
393
|
+
export declare function isAbsoluteZigDepPath(depPath: string): boolean;
|
|
301
394
|
export declare function normalizeZigDepPath(depPath: string): string | null;
|
|
302
395
|
/**
|
|
303
396
|
* The `root_source_file` paths a `build.zig` declares, dep-relative, with the
|
|
@@ -439,8 +439,15 @@ async function collectDeclaredNamespaces(filePath, declaredNamespaces, rootNames
|
|
|
439
439
|
// import whose namespace was dropped.
|
|
440
440
|
return structure.incomplete ? 'truncated' : 'ok';
|
|
441
441
|
}
|
|
442
|
-
const SWIFT_SOURCE_FACTORY_NAMES = [
|
|
443
|
-
|
|
442
|
+
const SWIFT_SOURCE_FACTORY_NAMES = [
|
|
443
|
+
'target',
|
|
444
|
+
'executableTarget',
|
|
445
|
+
'testTarget',
|
|
446
|
+
'macro',
|
|
447
|
+
'plugin',
|
|
448
|
+
];
|
|
449
|
+
/** No Swift sources: a prebuilt artifact or a C module map. */
|
|
450
|
+
const SWIFT_SKIP_FACTORY_NAMES = ['binaryTarget', 'systemLibrary'];
|
|
444
451
|
const SWIFT_SKIP_FACTORIES = new Set(SWIFT_SKIP_FACTORY_NAMES);
|
|
445
452
|
const SWIFT_FACTORY_RE = new RegExp(`\\.(${[...SWIFT_SOURCE_FACTORY_NAMES, ...SWIFT_SKIP_FACTORY_NAMES].join('|')})\\s*\\(`, 'g');
|
|
446
453
|
function extractBalancedParen(source, openIndex) {
|
|
@@ -550,7 +557,8 @@ function skipSwiftWsAndComments(source, start) {
|
|
|
550
557
|
return null;
|
|
551
558
|
}
|
|
552
559
|
/** First `name:` / `path:` string outside comments. Escapes and interpolations are unreadable. */
|
|
553
|
-
|
|
560
|
+
/** Where `field:`'s value starts in a factory block, or null when absent. */
|
|
561
|
+
function findSwiftFactoryFieldValue(block, field) {
|
|
554
562
|
let inString = null;
|
|
555
563
|
let escape = false;
|
|
556
564
|
let inLineComment = false;
|
|
@@ -615,18 +623,56 @@ function readSwiftFactoryField(block, field) {
|
|
|
615
623
|
i = j - 1;
|
|
616
624
|
continue;
|
|
617
625
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
626
|
+
return { valueAt: skipSwiftWsAndComments(block, colonAt + 1) };
|
|
627
|
+
}
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
function readSwiftFactoryField(block, field) {
|
|
631
|
+
const found = findSwiftFactoryFieldValue(block, field);
|
|
632
|
+
if (found === null)
|
|
633
|
+
return { value: undefined, keyPresent: false };
|
|
634
|
+
const { valueAt } = found;
|
|
635
|
+
if (valueAt === null)
|
|
636
|
+
return { value: undefined, keyPresent: true };
|
|
637
|
+
const quote = block[valueAt];
|
|
638
|
+
if (quote !== '"' && quote !== "'")
|
|
639
|
+
return { value: undefined, keyPresent: true };
|
|
640
|
+
const parsed = readSwiftSimpleQuotedString(block, valueAt);
|
|
641
|
+
return { value: parsed ?? undefined, keyPresent: true };
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* `sources:` / `exclude:` as a list of string literals. `undefined` when the
|
|
645
|
+
* key is absent; `null` when present but not a plain literal list.
|
|
646
|
+
*/
|
|
647
|
+
function readSwiftFactoryStringList(block, field) {
|
|
648
|
+
const found = findSwiftFactoryFieldValue(block, field);
|
|
649
|
+
if (found === null)
|
|
650
|
+
return undefined;
|
|
651
|
+
let i = found.valueAt;
|
|
652
|
+
if (i === null || block[i] !== '[')
|
|
653
|
+
return null;
|
|
654
|
+
const out = [];
|
|
655
|
+
for (;;) {
|
|
656
|
+
const at = skipSwiftWsAndComments(block, i + 1);
|
|
657
|
+
if (at === null)
|
|
658
|
+
return null;
|
|
659
|
+
if (block[at] === ']')
|
|
660
|
+
return out;
|
|
661
|
+
if (block[at] !== '"' && block[at] !== "'")
|
|
662
|
+
return null;
|
|
663
|
+
const value = readSwiftSimpleQuotedString(block, at);
|
|
664
|
+
if (value === null)
|
|
665
|
+
return null;
|
|
666
|
+
out.push(value);
|
|
667
|
+
const after = skipSwiftWsAndComments(block, at + value.length + 2);
|
|
668
|
+
if (after === null)
|
|
669
|
+
return null;
|
|
670
|
+
if (block[after] === ']')
|
|
671
|
+
return out;
|
|
672
|
+
if (block[after] !== ',')
|
|
673
|
+
return null;
|
|
674
|
+
i = after;
|
|
628
675
|
}
|
|
629
|
-
return { value: undefined, keyPresent: false };
|
|
630
676
|
}
|
|
631
677
|
/** Quoted literal with no escapes. Any `\` (including `\u{…}` and `\(`) is unreadable. */
|
|
632
678
|
function readSwiftSimpleQuotedString(source, openIndex) {
|
|
@@ -788,8 +834,11 @@ function swiftPathIsUnreadable(customPath, hasPathKey) {
|
|
|
788
834
|
/** Heuristic Package.swift scan. Never shells out to `swift package dump-package`. */
|
|
789
835
|
export function parseSwiftPackageManifest(source) {
|
|
790
836
|
const targets = new Map();
|
|
837
|
+
const implicitDirs = new Map();
|
|
838
|
+
const plugins = new Set();
|
|
839
|
+
const filters = new Map();
|
|
791
840
|
if (swiftManifestHasCompletenessHazard(source)) {
|
|
792
|
-
return { targets, complete: false };
|
|
841
|
+
return { targets, implicitDirs, plugins, filters, complete: false };
|
|
793
842
|
}
|
|
794
843
|
const packageTargets = inspectSwiftPackageTargets(source);
|
|
795
844
|
SWIFT_FACTORY_RE.lastIndex = 0;
|
|
@@ -832,22 +881,59 @@ export function parseSwiftPackageManifest(source) {
|
|
|
832
881
|
sawUnreadableFactory = true;
|
|
833
882
|
continue;
|
|
834
883
|
}
|
|
835
|
-
const
|
|
884
|
+
const sources = readSwiftFactoryStringList(block, 'sources');
|
|
885
|
+
const exclude = readSwiftFactoryStringList(block, 'exclude');
|
|
886
|
+
if (sources === null || exclude === null) {
|
|
887
|
+
sawUnreadableFactory = true;
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
if (sources !== undefined || exclude !== undefined) {
|
|
891
|
+
filters.set(name, {
|
|
892
|
+
...(sources !== undefined ? { sources } : {}),
|
|
893
|
+
...(exclude !== undefined ? { exclude } : {}),
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
const dirKind = kind === 'testTarget' ? 'test' : kind === 'plugin' ? 'plugin' : 'source';
|
|
897
|
+
if (dirKind === 'plugin')
|
|
898
|
+
plugins.add(name);
|
|
899
|
+
const dir = customPath ?? `${SWIFT_DEFAULT_TARGET_PARENT[dirKind]}/${name}`;
|
|
836
900
|
const existing = targets.get(name);
|
|
837
901
|
if (existing === undefined) {
|
|
838
902
|
targets.set(name, dir);
|
|
903
|
+
if (customPath === undefined)
|
|
904
|
+
implicitDirs.set(name, dirKind);
|
|
839
905
|
}
|
|
840
|
-
else if (customPath !== undefined &&
|
|
906
|
+
else if (customPath !== undefined && implicitDirs.has(name)) {
|
|
841
907
|
// A later `.target(name:path:)` wins over an earlier same-name
|
|
842
908
|
// factory that only implied the default path.
|
|
843
909
|
targets.set(name, customPath);
|
|
910
|
+
implicitDirs.delete(name);
|
|
844
911
|
}
|
|
845
912
|
}
|
|
846
913
|
return {
|
|
847
914
|
targets,
|
|
915
|
+
implicitDirs,
|
|
916
|
+
plugins,
|
|
917
|
+
filters,
|
|
848
918
|
complete: !sawUnreadableFactory && !packageTargets.helperBuilt,
|
|
849
919
|
};
|
|
850
920
|
}
|
|
921
|
+
/** Conventional parent of a target with no `path:`, before disk lookup. */
|
|
922
|
+
const SWIFT_DEFAULT_TARGET_PARENT = {
|
|
923
|
+
source: 'Sources',
|
|
924
|
+
test: 'Tests',
|
|
925
|
+
plugin: 'Plugins',
|
|
926
|
+
};
|
|
927
|
+
/**
|
|
928
|
+
* SwiftPM's predefined parents, in its search order (`PackageBuilder`):
|
|
929
|
+
* sources in `Sources`, `Source`, `src`, `srcs`; tests in `Tests` first, then
|
|
930
|
+
* the source parents; plugins only in `Plugins`.
|
|
931
|
+
*/
|
|
932
|
+
const SWIFT_PREDEFINED_TARGET_PARENTS = {
|
|
933
|
+
source: ['Sources', 'Source', 'src', 'srcs'],
|
|
934
|
+
test: ['Tests', 'Sources', 'Source', 'src', 'srcs'],
|
|
935
|
+
plugin: ['Plugins'],
|
|
936
|
+
};
|
|
851
937
|
const SWIFT_ALL_FACTORY_NAMES = new Set([
|
|
852
938
|
...SWIFT_SOURCE_FACTORY_NAMES,
|
|
853
939
|
...SWIFT_SKIP_FACTORY_NAMES,
|
|
@@ -1189,15 +1275,16 @@ function matchSwiftSquare(source, openIndex) {
|
|
|
1189
1275
|
}
|
|
1190
1276
|
return null;
|
|
1191
1277
|
}
|
|
1192
|
-
async function inferSwiftDirectoryTargets(repoRoot) {
|
|
1278
|
+
async function inferSwiftDirectoryTargets(repoRoot, packageDir = '') {
|
|
1193
1279
|
const targets = new Map();
|
|
1194
|
-
const sourceDirs = ['Sources', 'Package/Sources', 'src'];
|
|
1280
|
+
const sourceDirs = ['Sources', 'Source', 'Package/Sources', 'src', 'srcs'];
|
|
1195
1281
|
for (const sourceDir of sourceDirs) {
|
|
1196
1282
|
try {
|
|
1197
|
-
const fullPath = path.join(repoRoot, sourceDir);
|
|
1283
|
+
const fullPath = path.join(repoRoot, packageDir, sourceDir);
|
|
1198
1284
|
const entries = await fs.readdir(fullPath, { withFileTypes: true });
|
|
1199
1285
|
for (const entry of entries) {
|
|
1200
|
-
|
|
1286
|
+
// First parent in SwiftPM's search order wins a repeated name.
|
|
1287
|
+
if (entry.isDirectory() && !targets.has(entry.name)) {
|
|
1201
1288
|
targets.set(entry.name, sourceDir + '/' + entry.name);
|
|
1202
1289
|
}
|
|
1203
1290
|
}
|
|
@@ -1208,33 +1295,46 @@ async function inferSwiftDirectoryTargets(repoRoot) {
|
|
|
1208
1295
|
}
|
|
1209
1296
|
return targets;
|
|
1210
1297
|
}
|
|
1211
|
-
|
|
1298
|
+
/**
|
|
1299
|
+
* Load the SwiftPM config of the package at `packageDir` (repo root when
|
|
1300
|
+
* omitted). Target directories stay package-relative; see
|
|
1301
|
+
* {@link loadSwiftWorkspaceConfig} for the repo-relative merge.
|
|
1302
|
+
*/
|
|
1303
|
+
export async function loadSwiftPackageConfig(repoRoot, packageDir = '') {
|
|
1304
|
+
const pkgRoot = path.join(repoRoot, packageDir);
|
|
1212
1305
|
try {
|
|
1213
|
-
const source = await fs.readFile(path.join(
|
|
1306
|
+
const source = await fs.readFile(path.join(pkgRoot, await pickSwiftManifestFile(pkgRoot)), 'utf-8');
|
|
1214
1307
|
const parsed = parseSwiftPackageManifest(source);
|
|
1215
1308
|
if (parsed.complete) {
|
|
1216
1309
|
if (isDev) {
|
|
1217
1310
|
logger.info(`📦 Loaded ${parsed.targets.size} Swift package targets from Package.swift`);
|
|
1218
1311
|
}
|
|
1312
|
+
const parents = new Map();
|
|
1313
|
+
for (const [name, kind] of parsed.implicitDirs) {
|
|
1314
|
+
if (!parents.has(kind))
|
|
1315
|
+
parents.set(kind, await findSwiftPredefinedParent(pkgRoot, kind));
|
|
1316
|
+
const parent = parents.get(kind);
|
|
1317
|
+
if (parent != null)
|
|
1318
|
+
parsed.targets.set(name, `${parent}/${name}`);
|
|
1319
|
+
}
|
|
1320
|
+
// Plugins are modules (grouping) but never `import`-able.
|
|
1321
|
+
const declaredTargets = new Map([...parsed.targets].filter(([name]) => !parsed.plugins.has(name)));
|
|
1219
1322
|
if (parsed.targets.size > 0) {
|
|
1220
1323
|
return {
|
|
1221
1324
|
targets: parsed.targets,
|
|
1222
1325
|
origin: 'package.swift',
|
|
1223
|
-
declaredTargets
|
|
1326
|
+
declaredTargets,
|
|
1327
|
+
...(parsed.filters.size > 0 ? { targetFilters: parsed.filters } : {}),
|
|
1224
1328
|
};
|
|
1225
1329
|
}
|
|
1226
|
-
const inferred = await inferSwiftDirectoryTargets(repoRoot);
|
|
1227
|
-
return {
|
|
1228
|
-
targets: inferred,
|
|
1229
|
-
origin: 'package.swift',
|
|
1230
|
-
declaredTargets: parsed.targets,
|
|
1231
|
-
};
|
|
1330
|
+
const inferred = await inferSwiftDirectoryTargets(repoRoot, packageDir);
|
|
1331
|
+
return { targets: inferred, origin: 'package.swift', declaredTargets };
|
|
1232
1332
|
}
|
|
1233
1333
|
}
|
|
1234
1334
|
catch {
|
|
1235
1335
|
// Missing or unreadable — fall through to inferred folders.
|
|
1236
1336
|
}
|
|
1237
|
-
const inferred = await inferSwiftDirectoryTargets(repoRoot);
|
|
1337
|
+
const inferred = await inferSwiftDirectoryTargets(repoRoot, packageDir);
|
|
1238
1338
|
if (inferred.size > 0) {
|
|
1239
1339
|
if (isDev) {
|
|
1240
1340
|
logger.info(`📦 Inferred ${inferred.size} Swift source folders`);
|
|
@@ -1243,6 +1343,60 @@ export async function loadSwiftPackageConfig(repoRoot) {
|
|
|
1243
1343
|
}
|
|
1244
1344
|
return null;
|
|
1245
1345
|
}
|
|
1346
|
+
const SWIFT_VERSIONED_MANIFEST_RE = /^Package@swift-(\d+)(?:\.(\d+))?(?:\.(\d+))?\.swift$/;
|
|
1347
|
+
/**
|
|
1348
|
+
* The manifest SwiftPM would read in `pkgRoot`. A `Package@swift-X.Y.swift`
|
|
1349
|
+
* overrides `Package.swift` for toolchains at or above X.Y; the toolchain is
|
|
1350
|
+
* unknown here, so assume the newest and take the highest version present.
|
|
1351
|
+
*/
|
|
1352
|
+
async function pickSwiftManifestFile(pkgRoot) {
|
|
1353
|
+
let best = 'Package.swift';
|
|
1354
|
+
let bestVersion = [];
|
|
1355
|
+
let names;
|
|
1356
|
+
try {
|
|
1357
|
+
names = await fs.readdir(pkgRoot);
|
|
1358
|
+
}
|
|
1359
|
+
catch {
|
|
1360
|
+
return best;
|
|
1361
|
+
}
|
|
1362
|
+
for (const name of names) {
|
|
1363
|
+
const m = SWIFT_VERSIONED_MANIFEST_RE.exec(name);
|
|
1364
|
+
if (m === null)
|
|
1365
|
+
continue;
|
|
1366
|
+
const version = [Number(m[1]), Number(m[2] ?? 0), Number(m[3] ?? 0)];
|
|
1367
|
+
if (compareVersions(version, bestVersion) > 0) {
|
|
1368
|
+
best = name;
|
|
1369
|
+
bestVersion = version;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
return best;
|
|
1373
|
+
}
|
|
1374
|
+
function compareVersions(a, b) {
|
|
1375
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
1376
|
+
const d = (a[i] ?? -1) - (b[i] ?? -1);
|
|
1377
|
+
if (d !== 0)
|
|
1378
|
+
return d;
|
|
1379
|
+
}
|
|
1380
|
+
return 0;
|
|
1381
|
+
}
|
|
1382
|
+
/**
|
|
1383
|
+
* SwiftPM's parent directory for targets of `kind` in `pkgRoot`: the first
|
|
1384
|
+
* predefined parent that exists, chosen once per package (a target missing
|
|
1385
|
+
* from it is a manifest error in SwiftPM, not a fallthrough). Null when none
|
|
1386
|
+
* exists.
|
|
1387
|
+
*/
|
|
1388
|
+
async function findSwiftPredefinedParent(pkgRoot, kind) {
|
|
1389
|
+
for (const parent of SWIFT_PREDEFINED_TARGET_PARENTS[kind]) {
|
|
1390
|
+
try {
|
|
1391
|
+
if ((await fs.stat(path.join(pkgRoot, parent))).isDirectory())
|
|
1392
|
+
return parent;
|
|
1393
|
+
}
|
|
1394
|
+
catch {
|
|
1395
|
+
// Not there — try the next predefined parent.
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
return null;
|
|
1399
|
+
}
|
|
1246
1400
|
/**
|
|
1247
1401
|
* Load the Zig build configuration a repo's `build.zig.zon` + root `build.zig`
|
|
1248
1402
|
* declare: `.path` deps (and the roots their own build.zig names) from the
|
|
@@ -1563,7 +1717,7 @@ async function findZigPackageDirs(repoRoot) {
|
|
|
1563
1717
|
* nested-package branch exists to do; `normalizeZigDepPath` rejects the ones
|
|
1564
1718
|
* that still escape the ROOT after rebasing.
|
|
1565
1719
|
*/
|
|
1566
|
-
function isAbsoluteZigDepPath(depPath) {
|
|
1720
|
+
export function isAbsoluteZigDepPath(depPath) {
|
|
1567
1721
|
const normalized = depPath.replace(/\\/g, '/');
|
|
1568
1722
|
return normalized.startsWith('/') || /^[A-Za-z]:\//.test(normalized);
|
|
1569
1723
|
}
|
|
@@ -1,37 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Swift same-module implicit IMPORTS
|
|
3
|
-
* `emitImplicitImportEdges` hook.
|
|
2
|
+
* Swift same-module implicit IMPORTS for the `emitImplicitImportEdges` hook.
|
|
4
3
|
*
|
|
5
|
-
* Swift gives every file in a module
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* import pipeline (`emitImportEdges`) only materializes edges from
|
|
11
|
-
* finalized `ImportEdge`s — of which there are none here, because there
|
|
12
|
-
* is no syntactic `import`. This hook emits the missing edges directly.
|
|
4
|
+
* Swift gives every file in a module visibility of every other file's
|
|
5
|
+
* top-level declarations WITHOUT any `import` statement (whole-module
|
|
6
|
+
* visibility). There is no syntactic `import`, so the finalized-ImportEdge
|
|
7
|
+
* pipeline (`emitImportEdges`) has nothing to materialize; this hook records
|
|
8
|
+
* module membership directly.
|
|
13
9
|
*
|
|
14
|
-
* Module
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* and
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* assumption). An inferred folder map must keep sibling folders isolated.
|
|
23
|
-
* Every pair of distinct `.swift`
|
|
24
|
-
* files in the same module gets a directed IMPORTS edge in both directions
|
|
25
|
-
* (whole-module visibility is symmetric).
|
|
10
|
+
* Representation: one `Module` node per Swift module with at least two member
|
|
11
|
+
* files, and one File → Module IMPORTS edge per member, reason
|
|
12
|
+
* `MODULE_MEMBERSHIP_REASON`. This is how the compiler sees it — a file
|
|
13
|
+
* imports its own module — and it is linear. The earlier File → File form
|
|
14
|
+
* wrote n·(n−1) edges per module and exhausted V8's 2^24 Map limit on a 5,200
|
|
15
|
+
* file module (#3355). "Files that see file B" is now "files with a
|
|
16
|
+
* membership edge to a Module that B also has one to"; incremental importer
|
|
17
|
+
* expansion follows that rule (`queryImportersBatch`).
|
|
26
18
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
19
|
+
* A file compiled into several Xcode targets gets an edge to each module.
|
|
20
|
+
*
|
|
21
|
+
* The Module node's `filePath` is the target directory (SwiftPM), the
|
|
22
|
+
* `.xcodeproj` bundle (Xcode), or '.' (`__default__`). Incremental writeback
|
|
23
|
+
* keys rows by `filePath`: a rewritten member file pulls that key into the
|
|
24
|
+
* write set across its membership edge, so the Module node and every
|
|
25
|
+
* membership edge are deleted and rewritten together.
|
|
26
|
+
*
|
|
27
|
+
* Idempotent: node and edge ids are derived from the module key and the
|
|
28
|
+
* member path, and `graph.addNode` / `graph.addRelationship` dedupe by id.
|
|
33
29
|
*/
|
|
34
|
-
import type
|
|
30
|
+
import { type ParsedFile } from '../../../../_shared/index.js';
|
|
35
31
|
import type { KnowledgeGraph } from '../../../graph/types.js';
|
|
36
32
|
import type { GraphNodeLookup } from '../../scope-resolution/graph-bridge/node-lookup.js';
|
|
33
|
+
/** Graph id of the `Module` node for a Swift module key. */
|
|
34
|
+
export declare function swiftModuleNodeId(moduleKey: string): string;
|
|
37
35
|
export declare function emitSwiftImplicitImportEdges(graph: KnowledgeGraph, parsedFiles: readonly ParsedFile[], _nodeLookup: GraphNodeLookup, resolutionConfig?: unknown): void;
|