MCPcopy Create free account
hub / github.com/QodeXcli/QodeX / resolveSpecifier

Function resolveSpecifier

src/context/import-graph.ts:102–154  ·  view source on GitHub ↗
(
  spec: string,
  fromFile: string,
  fileSet: Set<string>,
  basenameIndex: Map<string, string[]>,
)

Source from the content-addressed store, hash-verified

100 * alias fallback.
101 */
102export function resolveSpecifier(
103 spec: string,
104 fromFile: string,
105 fileSet: Set<string>,
106 basenameIndex: Map<string, string[]>,
107): string | null {
108 // Relative import → resolve against the importing file's dir.
109 if (spec.startsWith('./') || spec.startsWith('../')) {
110 const baseDir = path.dirname(fromFile);
111 const joined = path.normalize(path.join(baseDir, spec));
112 // try exact, with extensions, and as a directory index
113 for (const ext of RESOLVE_EXTS) {
114 const cand = ext ? `${joined}${ext}` : joined;
115 if (fileSet.has(cand)) return cand;
116 }
117 for (const idx of INDEX_FILES) {
118 const cand = path.join(joined, idx);
119 if (fileSet.has(cand)) return cand;
120 }
121 return null;
122 }
123
124 // Python dotted intra-package (from .models import X handled above as ".models")
125 if (spec.startsWith('.')) {
126 const baseDir = path.dirname(fromFile);
127 const rel = spec.replace(/\./g, '/').replace(/^\//, '');
128 for (const ext of RESOLVE_EXTS) {
129 const cand = path.normalize(path.join(baseDir, rel + (ext || '.py')));
130 if (fileSet.has(cand)) return cand;
131 }
132 return null;
133 }
134
135 // Bare specifier: could be a path alias (@/store/cart, ~/lib/db) or an npm pkg.
136 // Strategy: take the last path segment and match by basename against project
137 // files. This resolves aliases without parsing tsconfig "paths", at the cost
138 // of occasionally matching a same-named file — acceptable for RAG widening
139 // (we're casting a slightly wider net, not doing a refactor).
140 const lastSeg = spec.split('/').pop()!;
141 const base = lastSeg.replace(path.extname(lastSeg), '');
142 const cands = basenameIndex.get(base);
143 if (cands && cands.length > 0) {
144 // Prefer a candidate whose path contains an earlier segment of the spec
145 // (e.g. "@/store/cart" → prefer .../store/cart.ts over .../cart.ts elsewhere).
146 const segs = spec.split('/').filter(s => s && !s.startsWith('@') && !s.startsWith('~') && s !== '.');
147 const scored = cands
148 .map(c => ({ c, score: segs.filter(s => c.includes(s)).length }))
149 .sort((a, b) => b.score - a.score);
150 // Only accept if at least the basename matched a real project file.
151 return scored[0]!.c;
152 }
153 return null; // external package
154}
155
156/** Build the import graph for a set of files. `read` lets callers inject cached content. */
157export async function buildImportGraph(

Callers 2

buildImportGraphFunction · 0.85

Calls 2

hasMethod · 0.45
getMethod · 0.45

Tested by

no test coverage detected