dsherret / dsherret/ts-morph

[ts-morph] Unable to get full qualified name of an identifier with path Mapping in tsconfig.json

Open
#1,430 3 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.