Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

90 lines
3.7 KiB
Go

package resolver
import "github.com/zzet/gortex/internal/graph"
// WorkspaceMembership maps a graph file path to the package-manager
// workspace that owns it. A monorepo is often a package-manager
// workspace — a root manifest (npm/yarn package.json, pnpm-workspace.yaml,
// Cargo.toml) that declares a set of member packages. Two member
// packages can legitimately define a symbol or package of the same
// name; when import resolution finds several same-named candidates the
// resolver prefers the one in the importing file's own workspace.
//
// Given a repo-prefixed graph file path the implementation returns an
// opaque workspace identifier — any stable string that is equal for two
// files in the same workspace and unequal across workspaces. It returns
// "" when the file belongs to no detected workspace (a plain repo, or a
// file outside every member directory); callers treat "" as "no
// workspace signal" and fall back to their pre-existing tie-break.
//
// The type lives in the resolver package so the resolver has no
// compile-time dependency on the indexer or the filesystem — the
// indexer builds a concrete implementation (which reads the workspace
// manifests from disk) and injects it via SetWorkspaceMembership.
type WorkspaceMembership func(filePath string) string
// SetWorkspaceMembership installs the package-manager workspace lookup
// used to break same-named import collisions. Pass nil to detach. Must
// be called before ResolveAll / ResolveFile — the resolver caches no
// workspace state across passes, so a mid-pass swap would race the
// parallel resolveEdge workers.
func (r *Resolver) SetWorkspaceMembership(fn WorkspaceMembership) {
r.workspaceMembers = fn
}
// SetWorkspaceMembership installs the package-manager workspace lookup
// on the cross-repo resolver. Same contract as the Resolver method.
func (cr *CrossRepoResolver) SetWorkspaceMembership(fn WorkspaceMembership) {
cr.workspaceMembers = fn
}
// preferSameWorkspaceFile picks, from a set of equally-ranked file
// candidates, the one that shares the importing file's package-manager
// workspace. It returns that candidate, or nil when no workspace lookup
// is installed, the importer belongs to no workspace, or zero / more
// than one candidate matches the importer's workspace (an ambiguous or
// empty result is left to the caller's existing tie-break).
//
// This is the same-workspace-preference rule for a name collision:
// when an import of a bare name (`logger`) matches a directory in two
// different workspace members, the member that belongs to the
// importer's own workspace wins.
func (r *Resolver) preferSameWorkspaceFile(callerFile string, candidates []*graph.Node) *graph.Node {
return pickSameWorkspaceFile(r.workspaceMembers, callerFile, candidates)
}
// preferSameWorkspaceFile is the CrossRepoResolver counterpart — same
// rule, same contract.
func (cr *CrossRepoResolver) preferSameWorkspaceFile(callerFile string, candidates []*graph.Node) *graph.Node {
return pickSameWorkspaceFile(cr.workspaceMembers, callerFile, candidates)
}
// pickSameWorkspaceFile is the shared body of the two
// preferSameWorkspaceFile methods.
func pickSameWorkspaceFile(fn WorkspaceMembership, callerFile string, candidates []*graph.Node) *graph.Node {
if fn == nil || callerFile == "" || len(candidates) < 2 {
return nil
}
callerWS := fn(callerFile)
if callerWS == "" {
return nil
}
var match *graph.Node
for _, n := range candidates {
if n == nil || n.Kind != graph.KindFile {
continue
}
if fn(n.FilePath) != callerWS {
continue
}
if match != nil {
// More than one candidate sits in the importer's
// workspace — the workspace does not disambiguate, so
// defer to the caller's existing tie-break.
return nil
}
match = n
}
return match
}