mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-08 03:08:13 +00:00
Merge 9a5637dcfe into a049b2dac6
This commit is contained in:
commit
3182f6b1e3
5 changed files with 84 additions and 37 deletions
|
|
@ -14,12 +14,13 @@
|
|||
* Module identity: Swift has no in-source `package X` marker. Module
|
||||
* membership is the SPM target *subtree* (`Sources/<Target>/…`), threaded
|
||||
* in via the SPM target map (`resolutionConfig` → `coerceSwiftTargets`)
|
||||
* and grouped by `groupSwiftFilesBySpmTarget` — replicating legacy
|
||||
* `groupSwiftFilesByTarget`. With no scanned source dir the map is null
|
||||
* and all files form one `__default__` module (single-Xcode-project
|
||||
* assumption). Every pair of distinct `.swift` files in the same module
|
||||
* gets a directed IMPORTS edge in both directions (whole-module
|
||||
* visibility is symmetric).
|
||||
* and grouped by `groupSwiftFilesBySpmTarget`. The helper preserves the
|
||||
* legacy `groupSwiftFilesByTarget` bucketing contract for ordinary layouts
|
||||
* while intentionally fixing #2931's repeated-prefix edge case. With no
|
||||
* scanned source dir the map is null and all files form one `__default__`
|
||||
* module (single-Xcode-project assumption). Every pair of distinct `.swift`
|
||||
* files in the same module gets a directed IMPORTS edge in both directions
|
||||
* (whole-module visibility is symmetric).
|
||||
*
|
||||
* Node identity + edge construction mirror the generic `emitImportEdges`
|
||||
* convention (`graph-bridge/imports-to-edges.ts`): `generateId('File', path)`
|
||||
|
|
|
|||
|
|
@ -9,20 +9,15 @@
|
|||
* drops cross-directory same-module edges and can mis-resolve a
|
||||
* constructor call to a wrong same-simple-named type in another target.
|
||||
*
|
||||
* This module duplicates the legacy `groupSwiftFilesByTarget`
|
||||
* (`languages/swift.ts`) semantics **verbatim** so the registry-primary
|
||||
* path matches legacy SPM-subtree grouping without touching the legacy
|
||||
* pipeline (hard constraint: legacy stays byte-identical). The SPM target
|
||||
* map is threaded in via the `resolutionConfig` channel
|
||||
* The SPM target map is threaded in via the `resolutionConfig` channel
|
||||
* (`loadSwiftPackageConfig` → `resolutionConfig` → these hooks); see
|
||||
* `scope-resolver.ts` and `scope-resolution/pipeline/run.ts`.
|
||||
*
|
||||
* NOTE: This intentionally differs from the import-config module's
|
||||
* leading-`startsWith` (`import-resolvers/configs/swift.ts`): that module
|
||||
* fans a file out to EVERY matching target (a nested file can belong to
|
||||
* multiple configured target dirs there), whereas legacy module grouping
|
||||
* assigns each file to the FIRST matching target only (legacy `break`s) —
|
||||
* one bucket per file. Do not copy the import-config behavior here.
|
||||
* multiple configured target dirs there), whereas module grouping assigns
|
||||
* each file to the FIRST matching target only — one bucket per file.
|
||||
*/
|
||||
|
||||
import type { SwiftPackageConfig } from '../../language-config.js';
|
||||
|
|
@ -30,15 +25,16 @@ import type { SwiftPackageConfig } from '../../language-config.js';
|
|||
const DEFAULT_TARGET = '__default__';
|
||||
|
||||
/**
|
||||
* Group `items` by SPM target subtree, replicating legacy
|
||||
* `groupSwiftFilesByTarget` semantics exactly:
|
||||
* Group `items` by SPM target subtree:
|
||||
*
|
||||
* - `targets` null/empty (no scanned source dir found) → ALL items go to
|
||||
* a single `__default__` bucket (single-Xcode-project assumption).
|
||||
* - Otherwise: a file matches a target when its normalized path either
|
||||
* starts with `<targetDir>/` (`indexOf === 0`) OR contains it at a `/`
|
||||
* boundary (`norm[idx - 1] === '/'`). Each file is assigned to the
|
||||
* FIRST matching target only (one bucket per file, no fan-out).
|
||||
* starts with `<targetDir>/` or contains `/<targetDir>/` at a segment
|
||||
* boundary. Using a segment-aware suffix search matters when an earlier,
|
||||
* non-boundary occurrence of the same text appears in the path (#2931).
|
||||
* - Each file is assigned to the FIRST matching target only (one bucket per
|
||||
* file, no fan-out).
|
||||
* - Files matching no target fall into the `__default__` bucket.
|
||||
*
|
||||
* `targets` is `name → directory` (the `SwiftPackageConfig.targets` map).
|
||||
|
|
@ -67,8 +63,7 @@ export function groupSwiftFilesBySpmTarget<T>(
|
|||
const normalized = rawPath.includes('\\') ? rawPath.replace(/\\/g, '/') : rawPath;
|
||||
let assigned = false;
|
||||
for (const { name, prefix } of targetPrefixes) {
|
||||
const idx = normalized.indexOf(prefix);
|
||||
if (idx === 0 || (idx > 0 && normalized[idx - 1] === '/')) {
|
||||
if (normalized.startsWith(prefix) || normalized.includes(`/${prefix}`)) {
|
||||
let group = groups.get(name);
|
||||
if (group === undefined) {
|
||||
group = [];
|
||||
|
|
|
|||
|
|
@ -10,10 +10,12 @@
|
|||
* Module identity: Swift has no in-source `package X` marker. The SPM
|
||||
* target is a directory subtree (`Sources/<Target>/…`). Module membership
|
||||
* is threaded in via the SPM target map (`ctx.resolutionConfig` →
|
||||
* `coerceSwiftTargets`) and grouped by `groupSwiftFilesBySpmTarget`,
|
||||
* replicating legacy `wireSwiftImplicitImports`'s `groupSwiftFilesByTarget`:
|
||||
* files are grouped by SPM target subtree when a package config is present,
|
||||
* else ALL Swift files form one module (`__default__`,
|
||||
* `coerceSwiftTargets`) and grouped by `groupSwiftFilesBySpmTarget`.
|
||||
* The helper preserves the legacy `wireSwiftImplicitImports` bucketing
|
||||
* contract for ordinary layouts (first target wins and unmatched files use
|
||||
* `__default__`) but intentionally fixes #2931's repeated-prefix edge case.
|
||||
* When a package config is present, files are grouped by SPM target subtree;
|
||||
* otherwise ALL Swift files form one module (`__default__`,
|
||||
* single-Xcode-project assumption). Every `.swift` file in the same target
|
||||
* sees its siblings' top-level defs.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import { groupSwiftFilesBySpmTarget } from '../../../src/core/ingestion/languages/swift/target-grouping.js';
|
||||
|
||||
describe('groupSwiftFilesBySpmTarget', () => {
|
||||
it('matches a later segment-aligned target prefix after an earlier partial occurrence', () => {
|
||||
const targets = new Map([['Core', 'Modules/Core']]);
|
||||
const items = ['vendor/SubModules/Core/shim/Modules/Core/Thing.swift'];
|
||||
|
||||
const groups = groupSwiftFilesBySpmTarget(items, (item) => item, targets);
|
||||
|
||||
expect(groups.get('Core')).toEqual(items);
|
||||
expect(groups.get('__default__')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('keeps an earlier segment-aligned match when a later occurrence is partial', () => {
|
||||
const targets = new Map([['Core', 'Modules/Core']]);
|
||||
const items = ['Modules/Core/Thing/SubModules/Core/Thing.swift'];
|
||||
|
||||
const groups = groupSwiftFilesBySpmTarget(items, (item) => item, targets);
|
||||
|
||||
expect(groups.get('Core')).toEqual(items);
|
||||
expect(groups.get('__default__')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('does not treat a partial path segment as a target match', () => {
|
||||
const targets = new Map([['Core', 'Modules/Core']]);
|
||||
const item = 'vendor/SubModules/Core/Thing.swift';
|
||||
|
||||
const groups = groupSwiftFilesBySpmTarget([item], (value) => value, targets);
|
||||
|
||||
expect(groups.get('Core')).toBeUndefined();
|
||||
expect(groups.get('__default__')).toEqual([item]);
|
||||
});
|
||||
|
||||
it('keeps first-target-wins behavior when target paths overlap', () => {
|
||||
const targets = new Map([
|
||||
['Outer', 'Sources'],
|
||||
['Inner', 'Sources/Feature'],
|
||||
]);
|
||||
const item = 'Sources/Feature/Thing.swift';
|
||||
|
||||
const groups = groupSwiftFilesBySpmTarget([item], (value) => value, targets);
|
||||
|
||||
expect(groups.get('Outer')).toEqual([item]);
|
||||
expect(groups.get('Inner')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -3,18 +3,20 @@
|
|||
* KTD2).
|
||||
*
|
||||
* `groupSwiftFilesBySpmTarget` (`languages/swift/target-grouping.ts`)
|
||||
* DUPLICATES the legacy `groupSwiftFilesByTarget` (`languages/swift.ts`)
|
||||
* SPM-subtree semantics so the registry-primary same-module hooks group by
|
||||
* the SPM target subtree without touching the legacy pipeline (hard
|
||||
* constraint: legacy stays byte-identical). Because the duplication can
|
||||
* silently drift if legacy is later changed, this test pins the exact
|
||||
* bucketing for representative inputs so a future divergence surfaces
|
||||
* loudly:
|
||||
* preserves the legacy `groupSwiftFilesByTarget` (`languages/swift.ts`)
|
||||
* bucketing contract for ordinary SPM layouts: one target bucket per file,
|
||||
* first-target-wins ordering, and the same `__default__` fallback. It now
|
||||
* intentionally differs for issue #2931's repeated-prefix edge case by
|
||||
* accepting a later segment-boundary occurrence when an earlier textual
|
||||
* occurrence is embedded inside a longer path segment. The legacy pipeline
|
||||
* remains byte-identical; these tests pin the shared behavior that must not
|
||||
* drift while allowing that documented correctness fix.
|
||||
*
|
||||
* 1. A multi-subdir single target buckets into ONE group.
|
||||
* 2. A file matching two overlapping same-named target prefixes is
|
||||
* assigned to the FIRST target only (legacy `break`s — no fan-out).
|
||||
* 3. Unmatched files AND the no-targets case route to `__default__` = all.
|
||||
* 3. Target dirs match only at path-segment boundaries.
|
||||
* 4. Unmatched files AND the no-targets case route to `__default__` = all.
|
||||
*
|
||||
* `coerceSwiftTargets` is also covered: it duck-types `{ targets: Map }`
|
||||
* (no `instanceof` on the config object) and returns `null` otherwise.
|
||||
|
|
@ -27,7 +29,7 @@ import {
|
|||
|
||||
const id = (s: string) => s;
|
||||
|
||||
describe('groupSwiftFilesBySpmTarget — legacy SPM-subtree parity (drift guard)', () => {
|
||||
describe('groupSwiftFilesBySpmTarget — shared SPM bucketing contract', () => {
|
||||
it('buckets a multi-subdir single target into ONE group', () => {
|
||||
const files = [
|
||||
'Sources/Alpha/Core/User.swift',
|
||||
|
|
@ -45,7 +47,7 @@ describe('groupSwiftFilesBySpmTarget — legacy SPM-subtree parity (drift guard)
|
|||
|
||||
it('assigns a file matching two overlapping same-named prefixes to the FIRST target only', () => {
|
||||
// Both targets are prefixes of the file's path (Beta dir nested under
|
||||
// Alpha). Legacy `break`s on the first match → one bucket per file.
|
||||
// Alpha). The first configured match wins → one bucket per file.
|
||||
const files = ['Sources/Alpha/Beta/User.swift'];
|
||||
const targets = new Map([
|
||||
['Alpha', 'Sources/Alpha'],
|
||||
|
|
@ -59,8 +61,8 @@ describe('groupSwiftFilesBySpmTarget — legacy SPM-subtree parity (drift guard)
|
|||
});
|
||||
|
||||
it('matches a target dir only at a `/` boundary, not a substring', () => {
|
||||
// "Sources/Alpha" must NOT match "Sources/AlphaBeta/..." — the legacy
|
||||
// predicate requires idx===0 or a preceding `/`.
|
||||
// "Sources/Alpha" must NOT match "Sources/AlphaBeta/...". The matcher
|
||||
// accepts only a path-start or slash-delimited target occurrence.
|
||||
const files = ['Sources/AlphaBeta/User.swift'];
|
||||
const targets = new Map([['Alpha', 'Sources/Alpha']]);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue