[ts-morph] Unable to get full qualified name of an identifier with path Mapping in tsconfig.json
- Dominant language
- TypeScript
- Stars
- 6.2k
- Forks
- 238
- Avg merge
- 2m
- Merged PRs (30d)
- 1
Description
**Unable to resolve the fullQualifiedName of a identifier in a function block**
"ts-morph": "^19.0.0",
Hi I have this use-case where I want to get the full qualified type of each function call happening inside a function, for some reason when the tsconfig.json has paths its not able to resolve the full qualified name properly even when I have a custom resolveModuleNames
tsconfig looks like this:
```
{
"exclude": ["./cypress", "./cypress.config.ts"],
"include": ["remix.env.d.ts", "reset.d.ts", "**/*.ts", "**/*.tsx"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
"types": ["vitest/globals"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"module": "CommonJS",
"moduleResolution": "node",
"resolveJsonModule": true,
"target": "ES2019",
"strict": true,
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"~/*": ["./app/*"]
},
"skipLibCheck": true,
// Remix takes care of building everything in `remix build`.
"noEmit": true
}
}
```
the project invocation for ts-morph looks like this:
```
tsProject = new Project({
tsConfigFilePath: config.typescriptTSConfig ?? undefined,
compilerOptions: {
...compilerOptions,
},
resolutionHost: (moduleResolutionHost, getCompilerOptions) => {
return {
resolveModuleNames: (moduleNames, containingFile) => {
return moduleNames.map(moduleName => {
let matchedPathKey: string | undefined = undefined;
for (const pathKey in pathMapping) {
const regex = new RegExp('^' + pathKey.replace('*', '(.*)'));
if (regex.test(moduleName)) {
matchedPathKey = pathKey;
break;
}
}
if (matchedPathKey === undefined) {
logger.appendLine(`Could not find a path mapping for ${moduleName} ${containingFile}`);
return undefined;
}
// Replace '*' with the captured substring from moduleName
const possiblePaths = pathMapping[matchedPathKey].map(path =>
path.replace('*', moduleName.replace(
new RegExp('^' + (matchedPathKey as string).replace('*', '(.*)')), '$1')
)
);
// Try to resolve the module path
for (const possiblePath of possiblePaths) {
let fullPath = undefined;
if (baseUrl !== undefined) {
fullPath = path.join(baseUrl, possiblePath);
} else {
fullPath = possiblePath;
}
const absolutePath = ts.resolveModuleName(
fullPath,
containingFile,
getCompilerOptions(),
moduleResolutionHost,
).resolvedModule;
if (absolutePath) {
logger.appendLine(`Resolved ${moduleName} to ${absolutePath.resolvedFileName}`);
return absolutePath;
}
}
// Fallback to default resolution
logger.appendLine(`Falling back to default Could not resolve ${moduleName} ${containingFile} to any of the following paths:` + possiblePaths.join(', '));
return ts.resolveModuleName(moduleName, containingFile, compilerOptions, moduleResolutionHost).resolvedModule;
});
}
};
},
});
```
And an example file is:
```
import { trimFormattedPhoneNumber, trimPhone } from "~/utils";
export function testing() {
trimFormattedPhoneNumber("1234567890");
}
```
here when I am at the node `trimFormattedPhoneNumber` inside the function `testing` I do `symbol.getFullyQualifiedName` but it just gives me back `trimFormattedPhoneNumber` missing out the whole import where its originally from?
I would have expected it to resolve the module and give me `utils.trimFormattedPhoneNumber` (notice the utils at the start)
Happy to provide more context as well. Thanks a lot!
Contributor guide
Research direction
Start by reproducing the example with the provided tsconfig.json, especially the baseUrl and paths mapping, and the ts-morph Project invocation with resolutionHost. Inspect the symbol at trimFormattedPhoneNumber inside testing() and compare getFullyQualifiedName with the expected imported-module qualification. Done means path-mapped imports produce the intended fully qualified name without breaking default resolution.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- compilers, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100