Fix importModuleSpecifier: "project-relative" ignored when tsconfig paths are configured#3953
Conversation
Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/76456a66-3d83-469d-b3a4-f8124623630d Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
importModuleSpecifier: "project-relative" ignored when tsconfig paths are configured
jakebailey
left a comment
There was a problem hiding this comment.
Seems like a bug? Not sure why projectDirectory would be the config file path, which is a filename, not a dir name.
There was a problem hiding this comment.
Pull request overview
Fixes a regression in module specifier selection so that ImportModuleSpecifierPreferenceProjectRelative correctly prefers relative paths for intra-project auto-imports even when tsconfig.json paths aliases exist.
Changes:
- Compute
projectDirectoryfrom the directory containingcompilerOptions.ConfigFilePath(not the config filename itself). - Always return the computed
relativePathfor intra-package imports in theRelativePreferenceExternalNonRelativebranch. - Add a new fourslash regression test + baseline for
project-relativewithpathsaliases.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
internal/modulespecifiers/specifiers.go |
Fixes project directory detection and ensures intra-package imports return relative specifiers under ExternalNonRelative. |
internal/fourslash/tests/importModuleSpecifierPreference_test.go |
Adds regression test covering project-relative with paths aliases. |
testdata/baselines/reference/fourslash/autoImports/importModuleSpecifierPreferenceProjectRelativeWithPaths.baseline.md |
Adds expected baseline showing a relative import is chosen. |
| var projectDirectory tspath.Path | ||
| if len(compilerOptions.ConfigFilePath) > 0 { | ||
| projectDirectory = tspath.ToPath(compilerOptions.ConfigFilePath, host.GetCurrentDirectory(), host.UseCaseSensitiveFileNames()) | ||
| projectDirectory = tspath.ToPath(tspath.GetDirectoryPath(compilerOptions.ConfigFilePath), host.GetCurrentDirectory(), host.UseCaseSensitiveFileNames()) |
There was a problem hiding this comment.
Switched the boundary check from strings.HasPrefix to tspath.Path.ContainsPath in be9152f, which has a proper separator-boundary check so /project vs /project2 aren't conflated. The TS reference uses plain startsWith here so it has the same latent issue, but since both projectDirectory and the source/module paths are already-normalized tspath.Path values, Path.ContainsPath is the natural fit on the Go side.
Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/e2c61b85-9d72-49ff-8cd4-0b0dfa20f979 Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
With
project-relativeand a tsconfig definingpathsaliases, intra-project auto-imports were emitting the alias (e.g.@utils/helper) instead of a relative path (e.g.../utils/helper).Two regressions in
getLocalModuleSpecifier(internal/modulespecifiers/specifiers.go) vs. the TS reference, both inside theRelativePreferenceExternalNonRelativebranch:projectDirectoryincluded the config filename. UsedcompilerOptions.ConfigFilePathdirectly instead of its directory, sosourceIsInternal/targetIsInternalwere alwaysfalse(no file path starts with…/tsconfig.json). This silently disabled the tsconfig-boundary check that decides when crossing the project root should prefer the alias.return relativePath. After the boundary checks confirm an intra-package import, TS returnsrelativePathunconditionally; the port gated it onlen(fromPackageJsonImports) > 0and otherwise fell through to the "fewer components wins" tiebreaker, which always chose the short paths alias over a deeper relative path.Changes
projectDirectoryastspath.GetDirectoryPath(compilerOptions.ConfigFilePath).fromPackageJsonImportsgate andreturn relativePathunconditionally at the end of theExternalNonRelativeblock.strings.HasPrefixtotspath.Path.ContainsPath, which enforces a separator boundary so siblings like/projectvs/project2aren't conflated.TestImportModuleSpecifierPreferenceProjectRelativeWithPathsreproducing the issue scenario (tsconfig withpaths, intra-project import,project-relative).The pre-existing
TestImportNameCodeFix_externalNonRelateive2was baselining the buggy behavior; with theprojectDirectoryfix the boundary check now fires correctly and its existing assertions (alias for cross-boundary, relative for intra-package) hold without changes.